home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1999 July / Macworld (1999-07).dmg / Shareware World / Info / For Developers / Mops 3.4.sea / Mops ƒ / Class < prev    next >
Text File  |  1999-02-20  |  79KB  |  2,935 lines

  1. \ High-level class/object implementation.
  2.  
  3. cr .( loading Class...)
  4.  
  5.  
  6. \ : >classxt            >classCfa  ;
  7. \ : aligned_addr?        cfa?  ;
  8.  
  9.  
  10. (*
  11. Note that the object header format is documented at "object building"
  12.  below.
  13.  
  14. Jan 96    mrh/imk    Added various mods to object initialization contributed
  15.                 by Ivo Krab.
  16.                 
  17. Jul 96    mrh        Mods made to support large_obj_array
  18.         mrh/rh    Incorporated bug fix from Reinout Heeck, so multiple
  19.                  records in unions work.
  20. Sep 96    mrh        Better inline{ - eliminating explicit out-of-line code
  21.                 8-way hashing of methods
  22.  
  23. ==============================================================================
  24.  
  25. Here are all our various class/object formats:
  26.  
  27.  
  28.  
  29.             ================= Object header ======================
  30.  
  31. Note if the obj is an ivar, it doesn't have a header if it's in a record,
  32. unless the ivar is indexed.  Indexed ivars always have headers, no matter
  33. what, since the indexing code relies on it.
  34.  
  35.  
  36. 2 bytes        Offset to the indexed area, rel to the class pointer
  37.             (which follows).  If not indexed, this will be 6.
  38.  
  39. 4 bytes        Class pointer (relocatable).
  40.  
  41. 2 bytes        Offset from the data start to the class pointer.
  42.             For simple objects (i.e. not embedded), this is -6.
  43.             For embedded objects, it will be more negative.  Note it
  44.             will always be negative.
  45.  
  46. (object's data starts here)
  47.  
  48. For indexed objects, the indexed area (after the ivars) is preceded by
  49. the indexed descriptor (xdesc) with this format:
  50.  
  51. 2 bytes        Width of indexed elements (in bytes)
  52. 4 bytes        Number of elements minus 1 (i.e. LIMIT-1).
  53.             The low word of this is used by a CHK instruction
  54.             if #elements is < 32K.
  55.  
  56. If indexing is attempted on a non-indexed object, the "offset to the
  57. indexed area" will be 6, taking us to the beginning of the object's
  58. data.  The CHK instruction will be done at offset -2 from there, which
  59. won't be the #elements, of course, but will be the offset to the
  60. class pointer WHICH IS ALWAYS NEGATIVE!!  Thus the CHK will always fail!
  61. This was a deliberate trick - about the only place in Mops I've
  62. resorted to anything like this, you'll be glad to know.  (At least I've
  63. described it for you!)
  64.  
  65. This trick has very limited usefulness now, since all the indexed
  66. methods are now defined in INDEXED-OBJ rather than OBJECT, so normally
  67. an indexed method on a non-indexed class wouldn't be found.  However
  68. the check comes for free, so I've retained it.
  69.  
  70.  
  71.         ==============  Class dictionary entry  ================
  72.  
  73. link/name/hndlr    as for normal colon definitions
  74. 4 bytes            call to BLD - the word which builds an object
  75. 32 bytes        links to 8-way hashed method chains (relative)
  76. 4 bytes            link to ivar chain (relative)
  77. 2 bytes            non-indexed data length
  78. 2 bytes            width of indexed elements, or zero if not indexed
  79. 2 bytes            flags
  80. 2 bytes            "xdispl offs" - the ivar offset where indexing starts
  81.                  (used by large_obj_arrays), or zero if none.
  82. 4(n+1) bytes    n-way to superclasses (n relocatable addrs terminated by zero)
  83.  
  84. Flag bits:
  85.     $0001        "large" - indexed with > 64K elements.
  86.     $0002        class is exported from a module
  87.  
  88.  
  89.         ==============  ivar dictionary entry  ================
  90.  
  91. 4 bytes        hashed name
  92. 4 bytes        link to prev ivar dic entry (relative addr)
  93. 4 bytes        class pointer (relocatable)
  94. 2 bytes        offset of this ivar's data from the base addr of the class
  95. 2 bytes        number of elements if indexed, or zero if not
  96. 2 bytes        flags
  97.  
  98. Flag bits: (zero is rightmost - what will we do on PowerPC?)
  99.  
  100. bit 0        1 = ivar gets an object header
  101. bit 1        1 = this is a static ivar
  102. bit 2        1 = this is a public ivar
  103.  
  104. Note: although indexed objects can have 2^^32 elements, we are
  105. assuming that an ivar can't have more than 64K elements.  This is
  106. because we are limiting the maximum ivar length of a class to 64K bytes,
  107. which is a stricter condition.  Would anybody want a longer ivar than
  108. this??
  109.  
  110.         ==============  Method dictionary entry  ================
  111.  
  112. 4 bytes        hashed name
  113. 4 bytes        link to prev method dic entry (relative addr)
  114. 2 bytes        flags
  115.  
  116.     (method code follows - this is the method's cfa here)
  117.  
  118. Flag bits:
  119.  
  120. bit 0        1 = private method (note other way round to ivars - we're using
  121.                 1 for the unusual case)
  122. bit 7        1 = there's a callFirst and/or callLast method
  123.  
  124.  
  125.         ==========================================================
  126. *)
  127.  
  128.  
  129. : xx  db ;            \ useful!
  130.  
  131.     26    constant    static_ivar_offs
  132.                             \ the offset from the start of the ivar dic
  133.                             \  info for a static ivar, to the ivar's data.
  134.                             \  The ivar info is 18 bytes long, then the
  135.                             \  ivar is instantiated immediately, with the
  136.                             \  usual 8-byte object header.  Total: 26.
  137.  
  138.     0    value    PUB/PRIV    \ -1 private, 1 public, 0 default - for ivars and methods
  139. false    value    STATIC?        \ true if following ivars are to be static
  140.     0    value    ^COMP_CLASS    \ addr of the class we're currently compiling
  141.     0    value    PIVAR        \ hashed name of any public ivar we're accessing
  142.     0    value    PIVSEL        \ hashed selector of any msg being sent to
  143.                             \  to a public ivar
  144.  
  145.     0    value    NEWOBJECT    \ addr of object being created
  146.     0    value    #SUP        \ number of superclasses for current class
  147.     0    value    SUPERS_TO_SKIP
  148.     0    value    INITID
  149.  
  150.  
  151. variable METAADDR            \ will hold relocatable address of pseudoclass
  152.                             \  Meta. Used in NW_IVSETUP to find if end
  153.                             \  of superclass chain has been reached
  154.  
  155.  
  156. \                ===============================
  157. \                        UTILITY WORDS
  158. \                ===============================
  159.  
  160. : PRIVATE        -1 -> pub/priv  ;        \ following methods and ivars will be private
  161. : PUBLIC         1 -> pub/priv  ;        \ following methods and ivars will be public
  162.  
  163. : END_PRIVATE    0 -> pub/priv  ;        \ back to the default
  164. : END_PUBLIC    0 -> pub/priv  ;        \ ditto
  165.  
  166.  
  167. : X    bld  123  ;                \ The 123 blocks optimization!
  168.  
  169. ' x @  forget x      constant    CLASSMK        \  JSR  bldVec-base(A3)
  170.  
  171. : EXBASE    $ 4E92  w,  ;    immediate    \  JSR  (A2)
  172.  
  173. : >OBJ  ( cfa -- ^obj )  inline{ 8 +}  ;
  174. : OBJ>  ( ^obj -- cfa )  inline{ 8 -}  ;
  175.             \ Note: we don't use >class here, since obj> shouldn't be
  176.             \ used for embedded objects, and it is used during obj
  177.             \ building when the ^class isn't there yet.
  178.  
  179. : CHKCLASS    \ ( cfa -- cfa )
  180.     class?  ?EXIT
  181.     .id  space  true ?error 80  ;
  182.  
  183. : ?>CLASS   ( ^obj -- ^class )
  184.     >class  dup 0= ?error 81  ;        \ If no legal class ptr, probably
  185.                                     \ not an obj addr at all!
  186.  
  187. \ the following offsets refer to where a ^class points, i.e. the cfa
  188. \ of the class.
  189.  
  190. (*    MFA_offset picks one of the 8 method threads, given a selID.
  191.     The selID is probably not very random in the low byte (since
  192.     selectors all end in ":", so we hash it a little more then pick
  193.     the 3 bits from the result which are already in the right position.
  194.  
  195.     Note: it took a surprising amount of trial and error to get a
  196.     good extra hash for this particular use!
  197. *)
  198.  
  199. : MFA_offset  ( selID ^class -- selID ^class MFA_offset )
  200.     over
  201.     dup 5 >> +
  202.     $ 1C and  4+  ;
  203.  
  204. : MFA  ( SelID ^Class -- SelID MFA )  MFA_offset  + ;
  205.  
  206. 36    constant    IFA_offset
  207.  
  208. : IFA    inline{ IFA_offset +} ;    \ ivar link
  209. : DFA    inline{ 40 +}    ;        \ Data len (2 bytes),
  210.                                  \  width of indexed elts (2 bytes)
  211. : FFA    inline{ 44 +}    ;        \ Flags (2 bytes)
  212. : XOFFA    inline{ 46 +}    ;        \ indexing offset for large_obj_arrays (2 bytes)
  213. : SFA    inline{ 48 +}    ;        \ Superclass N-way starts here
  214.  
  215. 48    constant    classSize        \ total size of class info up to N-way
  216.  
  217.  
  218. \ : GETDLEN        \ ( ^obj -- n )  Gets length of object's named ivars
  219. \    ?>class dfa w@  ;
  220.  
  221. : (^DLEN)    \ ( ^obj -- ^datalen )  This is a low-level word which should
  222.             \  normally only be used in the Mops system stuff.  Note it
  223.             \  takes ^obj, not ^class, and it doesn't do a module check
  224.             \  - it assumes the class is in the same segment as the object.
  225.     ?>class dfa  ;
  226.  
  227.  
  228. : (DLEN&XWID)    ( ^class -- dlen xwid )    \ Assumes ^class is the true class
  229.         dfa dup  w@  swap  2+ w@  ;        \  addres, not main dictionary address
  230.                                         \  of exported class in module
  231.                                         \ Only intended for internal use!
  232.  
  233. : DLEN&XWID        ( ^class -- dlen xwid )
  234.         ?>classInMod
  235.         (dlen&xwid)
  236.         ?unHoldMod  ;
  237.  
  238.  
  239. : DLEN    dlen&xwid  drop  ;
  240. : XWID    dlen&xwid  nip   ;
  241.  
  242. : IVARLEN    postpone dlen  ;    immediate        \ an alias for dlen
  243.  
  244. : OBJLEN    \ ( -- objlen )  Computes total data length of current object.
  245.  
  246.     ^base (^dlen)  dup w@        \ ivar len
  247.     swap 2+ w@  ?dup
  248.     IF            \ we're indexed
  249.         idxBase 4- @ 1+  * +    \ add len of indexed elements
  250.         6 +                        \  and len of indexed header
  251.     THEN
  252. ;
  253.  
  254.  
  255. : ?>MAINDIC  { ^class -- '^class }
  256.         \ If ^class is exported from a module, we return the main dic
  257.         \ equivalent.  If it's not exported, we return it unchanged.
  258.         \ We need this word since for exported classes, we need to use the
  259.         \ imported address (in the main dictionary) as the class pointer
  260.         \ in a new object or an ivar dic entry (so that the module will be
  261.         \ invoked properly when a method is sent to the object.
  262.  
  263.     ^class ffa 1+ 1 btest
  264.     IF        ^class >name n>count sfind drop
  265.     ELSE    ^class
  266.     THEN  ;
  267.  
  268.  
  269. : LARGE_OBJ_ARRAY_CHECK  { ^class offs \ xoffs  -- offs xdispl-offs }
  270.  
  271. \ Following <findm> or <IVfindm>, we check if this is a large_obj_array,
  272. \  in which case we might have to map the obj/ivar into the indexed area:
  273.  
  274.     ^class xoffa w@  -> xoffs    \ offs where remapping ends - are we before that?
  275.     ^class searchedClass <>
  276.     offs xoffs <  and
  277.     IF            \ yes - remapping necessary.  Return offs to xdispl ivar
  278.         offs  xoffs 12 +
  279.     ELSE        \ no - normal case - just return zero
  280.         offs 0
  281.     THEN
  282. ;
  283.  
  284.  
  285. : <findM>  { selID ^class \ cfa offs  -- cfa offs xdispl-offs }
  286.  
  287. (*    Factored out from clFndm and objFindm.  Finds a method's cfa given a
  288.     selID and a class address, which has already been converted to a module
  289.     addr if necessary.
  290.     
  291.     offs will be nonzero if the method turns out to belong to a superclass
  292.     with a non-zero offset in the object - i.e. an embedded object.
  293.     If it's a large_obj_array, and the object is in the indexed area,
  294.     xdispl-offs will be nonzero.  This allows the caller to compile
  295.     code to add the offset to the selected element.
  296. *)
  297.  
  298.     ^class -> objClass                    \ used in error msgs and inline binding
  299.  
  300.     selID ^class MFA_offset true  (findm)
  301.     NIF  cr  ^class .id  108 die        \ "method not found"
  302.     THEN
  303.     -> cfa  -> offs
  304.     offs -> emb_obj_offs                \ may need this in inline binding
  305.     cfa
  306.     ^class offs  large_obj_array_check
  307. ;
  308.  
  309.  
  310. : <findIV>  { selID ^class \ ^ivar offs xoffs -- ^ivar offs xdispl-offs T | -- F  }
  311.  
  312. (*    Basic routine to look for an ivar.  It's not an error if we don't find it,
  313.     so we return a flag.
  314. *)
  315.     selID ^class IFA_offset false (findm)  NIF  false  EXIT  THEN
  316.     8 -  -> ^ivar  -> offs        \ note - (findm) has returned the base
  317.                                 \  offs here.
  318.     ^ivar 12 + w@  ++> offs
  319.     ^ivar
  320.     ^class offs  large_obj_array_check
  321.     true
  322. ;
  323.  
  324.  
  325. : ClFindM  { selID ^class \ cfa offs xoffs -- cfa offs xdispl-offs }
  326.                                                 
  327. (*    finds a method's cfa given a selID and a class address, which hasn't
  328.     been checked for being in a module.  The returned results are as
  329.     described above for <findM>.
  330. *)
  331.     ^class ?>classInMod -> ^class
  332.     selID ^class  <findM>
  333. ;
  334.  
  335.  
  336. : ObjFindM  { selID ^obj \ ^class cfa offs xoffs  -- cfa offs xdispl-offs
  337.                                                 | -- cfa offs 0 }
  338.  
  339. (* Finds a method's xt given a selID and an obj addr.  The returned
  340.     results are as described above for <findM>.
  341. *)
  342.     ^obj >class  -> ^class
  343.     ^class NIF  81 die  THEN            \ "not an object"
  344.     selID ^class  <findM>
  345. ;
  346.  
  347.  
  348. : IVFindM    \ ( selID ^ivar -- xt offs xdispl-offs )
  349. \  Looks for a method in an ivar.
  350.  
  351.     8 + @abs        \ addr of ivar's class
  352.     clFindm  ;
  353.  
  354.  
  355. : SEND  { ^obj selID \ svMB -- }    \  Executes a method given its sel ID.  Used
  356.                                     \      in late binding.  Can also be used if you
  357.                                     \   have a dynamically determined method ID.
  358.     modBase -> svMB
  359.     selID ^obj  objFindM
  360.     ?dup
  361.     IF        ^obj + dup @ + +
  362.     ELSE    ^obj +
  363.     THEN
  364.     swap  ex-method
  365.     svMB -> modBase  ;
  366.  
  367.  
  368. : (DEFER)  ( ^obj -- )        \ Looks up SelID at IP and runs the method.
  369.                             \  Used in late binding.
  370.     @(ip)  send  ;
  371.  
  372.  
  373. 0 -> quitvec   0 -> abortvec   0 -> objInit        \ clear vectors
  374. ' pfind  -> ufind
  375.  
  376.  
  377. : ?CLASS        \ Error if not compiling a class definition.
  378.     cstate 0=  ?error 115  ;
  379.  
  380.  
  381.  
  382. (*    IVFIND is called when we've parsed a selector.  It determines if the next
  383.     word is an ivar.
  384.     Note: if found, <findIV> returns the equivalent of the cfa of
  385.     a method, which for ivars, is the addr of the class pointer.
  386. *)
  387.  
  388. : ivFind  { str-addr \ xdispl-offs -- ^ivar offs xdispl-offs T |  -- str-addr F }
  389.     str-addr
  390.     cstate  NIF  false  EXIT  THEN
  391.     hash ^comp_class  <findIV>        \ ( ^ivar offs xdispl-offs  T  |  F )
  392.     IF        true
  393.     ELSE    DP  false
  394.     THEN
  395. ;
  396.  
  397.  
  398. \ TOfind looks for a temp (local) object.
  399.  
  400. : TOfind  { str-addr -- ^ivar offs T | -- str-addr F  }
  401.     str-addr
  402.     tmpObjs  NIF  false  EXIT  THEN
  403.     hash
  404.     tmpObjs <findIV>
  405.     IF                    \ ( -- ^ivar offs xdispl-offs )
  406.             drop        \ xdispl-offs must be zero for class Dummy
  407.             dup $ FFFE >=
  408.             IF            \ self or super - mustn't match these in class Dummy!
  409.                 2drop  str-addr false  EXIT
  410.             THEN
  411.             true
  412.     ELSE    str-addr false
  413.     THEN
  414. ;
  415.     
  416. (*
  417. LocFind will be called from Ufind, which is the vector that gets first
  418. shot at recognizing a word.
  419. It looks at all the possibilities involving local names, which are
  420. not in the regular dictionary.  These possibilities are: named parms/locals,
  421. local objects, and if a class is being compiled, ivars of this class.
  422.  
  423. In the latter case, we arrange for the ivar's address to
  424. be pushed at run time simply by compiling ^base followed by an add of the
  425. ivar's offset - our code generation will produce optimal code for this.
  426. We then have to return the xt of some word to keep FIND happy - we don't
  427. need to compile anything else, so we use the xt of NULL and return a 1
  428. instead of True - this makes FIND think it's immediate.  So NULL is
  429. executed immediately, which does precisely nothing.
  430.  
  431. The one exception to this is if the "ivar" turns out to be SELF or SUPER
  432. - in this case we need to call the nucleus word SELF which works out
  433. the right base address (this is what happened pre-2.5).  Here we keep
  434. FIND happy by pushing the xt of SELF and True, so that it sees we've
  435. found SELF.
  436. *)
  437.  
  438. : LocFind        \ ( str-addr -- cfa T  |  -- str-addr F )
  439.     Pfind    ?dup  ?EXIT                    \ Found a named parm/local
  440.     TOfind
  441.     IF                                    \ Found temp obj
  442.         nip                                \ Don't need its dic addr
  443.         postpone locReg  postpone literal  postpone +
  444.         ['] null  1   EXIT
  445.     THEN
  446.  
  447. \ Now we look for an ivar name
  448.  
  449.     cstate  NIF  false  EXIT  THEN        \ search fails if we're not compiling
  450.                                         \  a class
  451.  
  452. \ mybugtest if db then
  453.     
  454.     dup hash ^comp_class IFA_offset false  (findm)
  455.     IF                                    \ Found ivar
  456.         nip nip                            \ don't need embedded obj offs or
  457.                                         \  string addr
  458.         4+ w@                            \ ivar offset
  459.         dup $ FFFE >=                    \ is it SELF or SUPER (just used in
  460.                                         \  isolation)?
  461.         IF    drop  ['] self  true  EXIT
  462.         THEN
  463.         postpone ^base postpone literal  postpone +
  464.         ['] null  1
  465.     ELSE    false
  466.     THEN  ;
  467.  
  468.  
  469. : ILFA     ( infa -- ilfa )    4+  ;
  470.  
  471.  
  472. : ^ICLASS  ( infa -- ^class | 0 )
  473.     8 + dup @ NIF   drop 0   ELSE   @abs ?>classInMod   THEN  ;
  474.  
  475.  
  476. : IOFFS    ( infa -- ioffs )    12 + w@  ;
  477. : I#ELS    ( infa -- #els )    14 + w@  ;
  478. : IFFA     ( infa -- iffa )    inline{ 16 +}  ;
  479.  
  480.  
  481. : ^NEXTIVAR    \ ( infa -- infa' )
  482.     ilfa  displace  ;
  483.  
  484.  
  485. \                        ========================
  486. \                                BINDING
  487. \                        ========================
  488.  
  489.     0    value    OBJ_BASE
  490.     0    value    OBJ_DISPL
  491.     0    value    OBJ_LOCAL_DISPL
  492.     0    value    OBJ_IND
  493.  
  494. false    value    SELF?
  495.  
  496.  
  497. : (OBJ)        \ Called from within an inline method.  Passes the object's
  498.             \  base and displacement to Handlers to generate the correct
  499.             \  address.  Optimization will then apply.
  500.  
  501.     obj_base obj_displ
  502.     obj_ind  genaddr
  503.     obj_local_displ  postpone literal  postpone +  ;
  504.  
  505.  
  506. : (IX)
  507.  
  508.     (*    Called from within an inline method.  Compiles code to generate
  509.         the indexed address.
  510.         ^comp_class has been set by inl_bind to the class of the obj
  511.         we're binding to.  One tricky point is that to access the indexed
  512.         area, we have to use the dlen value in this class, not the class
  513.         of the method we're calling (which may be a superclass).  But
  514.         the obj_local_displ has already had the embedded object offset
  515.         added in (if any).  We have to ignore this, since we're using 
  516.         the object's class, not the method's.  When the method was found,
  517.         the value emb_obj_offs was set to this offset, so we subtract
  518.         it here.
  519.     *)
  520.  
  521.     ^comp_class dlen&xwid  swap
  522.     self?
  523.     IF  drop  -1  ELSE  aligned  6 +  THEN
  524.     obj_base obj_displ obj_local_displ
  525.     emb_obj_offs -
  526.     obj_ind  ^comp_class ffa w@
  527.     genxaddr  ;
  528.  
  529.  
  530. : ^BASE
  531.     compinline?
  532.     IF        (obj)
  533.     ELSE    postpone ^base
  534.     THEN  ;            immediate
  535.  
  536.  
  537. : ^ELEM
  538.     compinline?
  539.     IF        (ix)
  540.     ELSE    postpone ^elem
  541.     THEN  ;            immediate
  542.  
  543.  
  544. : OBJ    postpone ^base  ;    immediate        \ for backward compatibility
  545. : IX    postpone ^elem  ;    immediate        \ ditto
  546.  
  547.  
  548. local  EARLY_BIND  { oCfa oBase oDispl oLDispl oind slf? \ ^mod ptr -- }
  549.  
  550.  
  551. : INL_BIND    \ ( -- b )
  552.     \ In-line code to be compiled for this method.
  553.     \ But note, we don't do it if obj_base is zero, meaning that
  554.     \ we have put the ^obj in A0 as a temporary.  Some inline
  555.     \ methods could cause a clash on A0.  So in this case we
  556.     \ call the out-of-line code - we return true so that this
  557.     \ will be done by NORM_BIND.  Otherwise we return false.
  558.  
  559.     obj_base
  560.     NIF                                    \ Update cfa to the out-of-line code
  561.         oCfa 2+ dup c@ + aligned  -> oCfa  true
  562.     ELSE
  563.         ^comp_class  cstate  self?                \ Save over upcoming evaluate
  564.         slf? NIF  objClass -> ^comp_class  THEN    \ Set ^comp_class and cstate
  565.         true -> cstate                            \  so ivars are accessible
  566.         slf? -> self?
  567.         oCfa  (compinl)
  568.         -> self?  -> cstate  -> ^comp_class        \ Restore
  569.         false
  570.     THEN  ;
  571.  
  572.  
  573. : SETUP_MODULE_BIND
  574.     heldMod
  575.     @ @            \ get mod handle and dereference - addr of mod start
  576.     SAmask and  -> ^mod
  577.     ^mod 8 + -> ptr            \ self-rel addr of exports table
  578.     ptr @ ++> ptr            \ ptr -> start of table
  579.     0 -> methIndex
  580.     BEGIN
  581.         ptr @ dup 0< ?error 198
  582.         ^mod +  oCfa =
  583.     NWHILE
  584.         4 ++> methIndex  4 ++> ptr
  585.     REPEAT
  586. ;
  587.     
  588.  
  589. : NORM_BIND
  590.     heldMod IF  setup_module_bind  THEN
  591.     oCfa  (obj)  EB  ;
  592.  
  593.  
  594. :loc  EARLY_BIND        \ { oCfa oBase oDispl oLDispl oind slf? -- }
  595.     obj_base  obj_displ  obj_local_displ  obj_ind        \ Save
  596.     oBase    -> obj_base            oDispl    -> obj_displ
  597.     OLdispl    -> obj_local_displ    oind    -> obj_ind
  598.     oCfa w@  inlMk =
  599.     IF  inl_bind  ELSE  true  THEN
  600.     IF  norm_bind  THEN
  601.     -> obj_ind  -> obj_local_displ
  602.     -> obj_displ  -> obj_base                            \ Restore
  603. ;loc
  604.  
  605.  
  606. : BIND_TO_OBJ  { cfa ^obj offs -- }
  607.     cfa
  608.     -1                    \ -1 as "base" signals handlers to generate
  609.     ^obj                \  a normal dic addr.  We still carry the
  610.                         \  offs here since if we need to access the
  611.                         \  indexed area, we want the original obj addr,
  612.                         \  not some embedded object.
  613.     offs  0  false  early_bind  ;
  614.  
  615. : BIND_TO_STK        \ ( cfa -- )
  616.     stkObj  0 swap  false  early_bind  ;
  617.  
  618. : BIND_TO_IVAR  { cfa offs -- }
  619.     cfa  obj_base  obj_displ
  620.     obj_local_displ offs +
  621.     obj_ind  false  early_bind  ;
  622.  
  623. : BIND_TO_TMPOBJ  { cfa offs -- }
  624.     cfa
  625.     4        \ locReg = D4
  626.     offs
  627.     0 0 false  early_bind  ;
  628.  
  629. : BIND_TO_SELF  { cfa offs -- }
  630.     cfa  obj_base  obj_displ  offs  obj_ind  true  early_bind  ;
  631.  
  632.  
  633.  
  634. \                    ===========================
  635. \                     INITIALIZING NEW OBJECTS
  636. \                    ===========================
  637.  
  638. false    value    REC?        \ Are we compiling a record?
  639. false    value    UNION?        \ Are we compiling a union in a record?
  640.     0    value    UNIONOFFS    \ Base offset of the current union
  641.  
  642.  
  643. : INIT_OBJ   ( theClass theObj -- )
  644.  
  645. (*    Performs CLASSINIT: method on object.  Note, we quite deliberately don't 
  646.     check if the offset would put us into the indexed area of a large_obj_array.
  647.     This is because we don't want to send CLASSINIT: individually to each of the
  648.     indexed elements, but instead we just send it to the base element.  Then,
  649.     CLASSINIT: in the large_obj_array class copies this to the indexed elements.
  650. *)
  651.  
  652.     swap
  653.     ( theObj theClass )        initID swap MFA_offset true (findm)
  654.     ( theObj offs xt true )    drop     \ Is guaranteed to find CLASSINIT: method
  655.     ( theObj offs xt  )        >r + r>    \ Modify obj addr by offs (needed in case
  656.                                     \  method is defined in any superclass
  657.                                     \  but the first)
  658.     ( theObj' xt )          ex-method
  659. ;
  660.  
  661.  
  662. : MAKE_HDRS        ( #els ) { theClass theObj \ len wid -- }
  663.     \ assumes theClass is the true class address, not
  664.     \ the main dictionary address of an exported class
  665.     \ if theClass is not indexed, there should be no #els on the stack
  666.  
  667.         ASSERT{ theObj 1 and 0= }        \ obj addr must be aligned if this
  668.                                         \  word is called
  669.  
  670.         theClass (dlen&xwid)  -> wid  -> len            
  671.                                                 
  672.     \ first the xdesc (indexed area header), if indexed object
  673.         wid     IF        len aligned -> len
  674.                         theObj len +        \ xdesc address: after ivars
  675.         ( #els ^xdesc )    wid over w!            \ two bytes: indexed width
  676.         ( #els ^xdesc )    swap 1- swap 2+ !    \ four bytes: limit ( #els-1)
  677.                         len 12 +            \ offset to indexed area
  678.                                             \  to be put in obj header
  679.                 ELSE    6                    \ standard offset if not indexed
  680.                 THEN
  681.  
  682.         \ now the obj header itself
  683.         ( offs )    theObj 8 - w!        \ 2 bytes: offset to indexed area
  684.                                         \  calculated above
  685.                     theClass ?>maindic    \ don't store module addr of class!
  686.                     false -> relocChk?    \ obj address could be in heap!
  687.         ( ^class )    theObj 6 - reloc!    \ 4 bytes: relocatable class pointer
  688.                     true -> relocChk?
  689.                     -6 theObj 2- w!        \ 2 bytes: offset to class pointer --
  690.                                         \  always -6 for non-embedded object
  691. ;
  692.  
  693. forward IVSETUP
  694.  
  695. : NW_IVSETUP  { ^nway baseOffs EOoffs \ initEOoffs supClass supOffs -- }
  696.  
  697. (*    Sets up the groups of ivars for each superclass of the current object/ivar
  698.     being processed. One group for each super of a multiply inherited object.
  699.     Each group we call an "embedded object", which sort of describes what it is.
  700.     On entry ^nway points to the first superclass pointer in the n-way defining
  701.     the multiple inheritance. We repeat the procedure for each superclass until
  702.     the zero marking the end of the n-way is encountered. If the superclass
  703.     is the pseudoclass Meta we don't do anything since it does not have any ivars.
  704.     baseOffs is the position of the current object/ivar's data space relative
  705.     to newObject, the current top-level object being created.
  706.     EOoffs is the offset from newObject at which the current Embedded Object
  707.     starts. When an embedded object starts at a non-zero EOoffs, we put in
  708.     front of it a 2-byte offset to the class pointer. Note that if the
  709.     multiply inherited object is an ivar, there may not be a class pointer!
  710.     This doesn't matter, since it's better for multiply inherited
  711.     objects to always have the same format, wherever they are, and any attempt
  712.     to use the class pointer offset to get the (nonexistent) class pointer
  713.     will most probably be caught by our checks.
  714. *)
  715.  
  716. \ From Mops 2.5 on, we're now sending classinit: separately to each
  717. \  superclass.
  718.  
  719.     EOoffs -> initEOoffs
  720.     BEGIN
  721.         ^nway @abs ?>classInMod  -> supClass    \ may hold a mod
  722.         supClass  MetaAddr @abs  <>
  723.         IF
  724.             baseOffs EOoffs +  initEOoffs -    \ Start of dataspace of this
  725.             -> supOffs                        \  superclass
  726.             supClass ifa displace            \ Infa of first ivar of supClass
  727.             supOffs  EOoffs  ivSetup
  728.             supClass  newObject supOffs +  init_obj
  729.         THEN
  730.         ?unholdMod                                \ now finished with the mod
  731.         1cell ++> ^nway
  732.         ^nway @
  733.     WHILE        \ another class coming up - store 2-byte ^class offset first
  734.         supClass dfa w@                \ dlen of supClass. Faster than using DLEN
  735.         ( dlen ) aligned ++> EOoffs
  736.         EOoffs negate 8 -            \ ^class offset for store
  737.         EOoffs initEOoffs -            \ offset not already included in baseOffs
  738.         baseOffs + newObject +        \ final addr for store
  739.         w!
  740.         2 ++> EOoffs
  741.     REPEAT  ;
  742.  
  743.  
  744. (*    IVsetup recursively traverses the tree of nested ivar definitions in a
  745.     class, building headers and indexed area headers where necessary, and 
  746.     sending    the CLASSINIT: message to each ivar.
  747.     
  748.     On entry infa is the nfa of the first ivar in the ivar dictionary of the
  749.     object/ivar whose (sub)ivars we are to set up. The dictionary chain is
  750.     followed to the end, the last link pointing to the Nway superclass pointer.
  751.     baseOffs is the position of the current object/ivar's data space relative
  752.     to newObject, the current top-level object being created.
  753.     EOoffs is non-zero if the ivar whose subivars we are to set up is part
  754.     of an "embedded object", ie. is inherited from a superclass, and this
  755.     superclass is not the first super of the current top-level object.
  756.     This is passed on unmodified in any recursive call and used only by
  757.     NW_IVSETUP to calculate the offset to the class pointer.
  758.     When this word is called, if the object/ivar's class is in a module,
  759.     the module will be held. In some circumstances the caller still needs it.
  760.     The recursive call might require another module to be held, so we have to
  761.     save and restore any module held on entry.
  762. *)
  763.  
  764. :f IVSETUP        { infa baseOffs EOOffs \ ivOffs ivClass -- }
  765.  
  766.     heldMod                \ If class is in module it must not get unheld
  767.                         \  while processing so keep address on the stack
  768.     0 -> heldMod        \  and clear heldMod so it cannot be unheld
  769.         
  770.     BEGIN
  771.         infa @ 0>    \ A selector is always negative, so a
  772.                     \  positive value means the N-way superclass
  773.                     \  pointer area ( superclass adresses ),
  774.                     \  the endpoint of the ivar dictionary chain
  775.                     
  776.     NWHILE    \ build this ivar in object
  777.  
  778.         infa iffa w@ 2 and            \ Static ivar? -> not in obj (bit 1)
  779.         NIF    infa ioffs                \ Offset of ivar in owning object
  780.             baseoffs +   -> ivOffs    \ Position relative to newObject
  781.             infa ^iclass -> ivClass    \ May cause another module to be held
  782.             infa iffa w@ 1 and        \ Does it want headers? -> flag bit 0
  783.             IF    infa i#els dup NIF drop THEN
  784.                 ivClass
  785.                 newObject ivOffs +     \ address where headers must be made
  786.                 make_hdrs
  787.             THEN
  788.             ?Rdepth                    \ Check on recursion depth
  789.             ivClass ifa displace    \ Infa of first subivar in
  790.                                     \  chain of the currently
  791.                                     \  processed ivar object
  792.             ivOffs                    \ New base offset of subivar
  793.             0
  794.             ivSetup
  795.             ?unholdMod
  796.             ivClass  newObject ivOffs +  init_obj
  797.         THEN
  798.         infa ^nextivar -> infa
  799.     REPEAT
  800.     infa baseOffs EOoffs  NW_ivSetup    \ Set up superclasses
  801.     ( Heldmod )  -> HeldMod
  802. ;f
  803.  
  804.  
  805. \ HASHED-HDR lays down the dic header for an ivar or method.
  806. \ The format is:
  807. \
  808. \ 4 bytes        hash
  809. \ 4 bytes        link (self-relative addr of prev entry)
  810. \
  811. \ This entry has to become the first on the chain, so we pass in the
  812. \ addr of the chain header.
  813.  
  814. : HASHED-HDR        \ ( chain-hdr hash-val -- )
  815.     ,                        \ comma in hash value
  816.     dup displace            \ get abs addr of prev entry
  817.     displ,                    \ comma it in as self-relative addr
  818.     here 8 -  swap  displ!    \ update chain header
  819. ;
  820.  
  821.  
  822. forward    DIC-OBJ
  823.  
  824. : IVDEF  ( #els ) { iclass \ #els wid siz clOffs flags -- }
  825.         \ Compiles an ivar dictionary entry.  If indexed, must have
  826.         \ < 64K elements.  iclass is the ivar's class.  The class of
  827.         \ which this is an ivar, is pointed to by ^class.
  828.  
  829.     pub/priv 1 =  4 and -> flags    \ initial flags - set bit 2 if we're public
  830.     Mword
  831.     ivFind  ?error 117                \ same name as another ivar
  832.     drop
  833.     iclass xwid  -> wid                \ indexed width of ivar class
  834.     iclass dlen  -> siz                \ non-indexed size of this ivar
  835.     
  836. \ The initial offset is the current dlen of the class.
  837.  
  838.     ^comp_class dfa w@  -> clOffs
  839.     
  840.     ^comp_class  ifa
  841.     here  hash  hashed-hdr            \ dic header for ivar
  842.  
  843.     iclass ?>mainDic  reloc,
  844.     
  845. \ Now we need to comma in the 2-byte offset to the ivar within
  846. \ the class.  First we need to make some adjustments...
  847. \ Do we need to align the offset?
  848.  
  849.     siz 1 >                \ we do if the ivar size is longer than 1
  850.     wid                    \ or if it's indexed
  851.     or
  852.     IF                \ We do need to align the offset. Note that if the
  853.                     \ ivar class is multiply inherited with >1 superclass
  854.                     \ of non-zero length, the ivar size will always be >1.
  855.         clOffs aligned  -> clOffs
  856.     THEN
  857.  
  858.     iclass ffa 1+ 2 btest        \ general?
  859.     dup IF union? ?error 190 THEN    \ (can't have a general object in a union)
  860.     rec? not or                    \ or not in a record?
  861.     wid or                        \ or indexed?
  862.     IF                            \ Yes.  In this case the ivar will have
  863.                                 \  the standard 8-byte object header. This
  864.         clOffs aligned            \  must be aligned, so we first align in
  865.         -> clOffs                \  case we didn't do it above.
  866.         8 ++> clOffs            \ Then the ivar's data will start 8 bytes
  867.                                 \  later than otherwise.
  868.         1 or> flags                \ and we'll mark this in the ivar flags
  869.                                 \  so make_hdrs will do the right thing.
  870.     THEN
  871.     clOffs  w,
  872.  
  873.     wid
  874.     IF                \ Indexed. Stack has #els.  We calculate the indexed
  875.                     \ length of this ivar and increment clOffs.
  876.                     \ If we're not in a record, we also need to align the
  877.                     \ non-indexed size of the ivar, since the xdesc must
  878.                     \ be aligned. (If we're in a record, there won't be an
  879.                     \ xdesc.)
  880.         -> #els
  881.         siz aligned  -> siz            \ must align the non-indexed size
  882.         #els w,                        \ Add #els to ivar dic entry
  883.         #els wid *                    \ Get indexed length
  884.         6 +                            \ Add 6 for xdesc length
  885.         ++> clOffs                    \ Add to clOffs
  886.     ELSE            \ Not indexed.
  887.         0 w,
  888.     THEN
  889.     static?
  890.     IF    2 or> flags
  891.     ELSE
  892.         siz ++> clOffs                \ Bump clOffs by non-indexed size of ivar
  893.     THEN
  894.     flags w,
  895.  
  896. (* Now we'll update the class dLen field by whatever we're allocating for this
  897.   ivar - it will then be the offset to the next ivar.  clOffs has the offset
  898.   so far.  In the normal case, this is what goes in dLen.  If we're in
  899.   a union, we MAX it with whatever's already in dLen.  This will leave dLen
  900.   with the longest union element we've reached so far, which will be the final
  901.   value in case we hit the end of the union.
  902.   And if this ivar is static, it will live right where we are in the dic,
  903.   and not in objects of the class, so in this case we leave dLen alone.
  904. *)
  905.     union?
  906.     IF        unionOffs  clOffs  max  -> unionOffs
  907.     ELSE    
  908.         static?
  909.         NIF    clOffs  ^comp_class dfa w!
  910.         THEN
  911.     THEN
  912.  
  913. (* Now we'll check if this ivar is to be static - if so, we'll instantiate
  914.    it right here.
  915. *)
  916.  
  917.     static?  0EXIT
  918.     wid IF  #els  THEN
  919.     iclass  dic-obj
  920. ;
  921.  
  922.  
  923. \                    =================================
  924. \                            OBJECT BUILDING
  925. \                    =================================
  926.  
  927.  
  928. : CL>LEN ( #els ) { theClass \ wid len -- ( #els ) len2 }
  929.                 \ Gets data length of object given #els and class.
  930.     theClass dlen&xwid  -> wid  -> len
  931.     wid IF    ( #els )  dup 32766 >
  932.         IF  theClass ffa 1+ 0 btest 0= ?error 185  then
  933.          dup  wid *  6 +  len +
  934.     ELSE    len
  935.     THEN  ;
  936.  
  937.  
  938. : MAKE_OBJ        ( #els ) { theClass theObj \ svHeldMod -- }
  939.  
  940.     theClass ?>classinMod -> theClass    \ Need real class address,
  941.                                         \  not main dic equivalent
  942.     heldMod -> svHeldMod                \ If class is in module it must
  943.                                         \  not get unheld while processing
  944.                                         \  so keep the address and clear
  945.     0 -> heldMod                        \  heldMod so it cannot be unheld
  946.   ( #els ) theClass theObj make_hdrs    \ Actually #els is optional element
  947.                                         \  on the stack
  948.         
  949.     theObj -> newObject                    \ base address used by ivsetup
  950.     theClass ifa displace  0 0 ivSetup
  951.     svHeldMod -> heldMod  ?unholdMod    \ held module (if any) no longer needed
  952.     theClass theObj init_obj            \ do a latebound CLASSINIT:
  953. ;                                        \  on the object
  954.  
  955.  
  956. :f DIC-OBJ  ( #els ) { theClass \ ^obj -- }
  957.                 \ Builds an object in the dictionary.
  958.     here >obj -> ^obj                \ Where obj data will start
  959.     theClass  cl>len
  960.     8 +  aligned                    \ Required length
  961.     dup room >  ?error 186            \ "Not enough room"
  962.       reserve                            \ Allocate space for object
  963.     theClass  ^obj  make_obj        \ Set up the object
  964.     align-dp  ;f
  965.  
  966.  
  967. :f BLD        \ ( (#els) -- )    \ Builds an object.
  968.                             \ Gets called when a class name
  969.                             \  is executed
  970.     r> 4-    \ Trick! pulling the return address from the stack
  971.             \  causes exit to skip the rest of the calling word,
  972.             \  which is actually a class definition and does not
  973.             \  contain any more executable code.
  974.             \  Subtracting 4 gives the class cfa, needed later
  975.             \ Note: because of this trick we can't use locals here!
  976.  
  977.     cstate    IF        ( theClass )  ivDef        \ Build an ivar
  978.             ELSE    create_obj                \ Create object header - returns
  979.                                             \  its data address when called
  980.                     ( theClass )  dic-obj
  981.             THEN
  982. ;f
  983.  
  984.  
  985.  
  986.  
  987. : ]C    true  -> cstate ;        immediate
  988. : C[    false -> cstate ;        immediate
  989.  
  990.  
  991. : HASH,        \ Compiles hashed word for name at here
  992.     @word  hash ,  ;
  993.  
  994.  
  995. \                    ============================
  996. \                            :CLASS  etc.
  997. \                    ============================
  998.  
  999.  
  1000. \ Here we set up some quantities so that we can send messages to SELF
  1001. \ or SUPER.  These are treated syntactically as ivars, so to implement
  1002. \ them we actually set up dummy ivars SELF and SUPER.
  1003.  
  1004. \ When we're processing a :CLASS definition, we plug the appropriate
  1005. \ addresses into these ivars.  ^SELF is a word defined to return the
  1006. \ addr of the dummy ivar SELF, so we can do the plugging.
  1007. \ In the case of SUPER, there may be several superclasses, so we have
  1008. \ to go through a class descriptor, since that's the only place we look
  1009. \ for an n-way (a set of addresses).  So we set the "class" of SUPER
  1010. \ to a dummy class SUPCL, which has no ivars or methods (so the search
  1011. \ will pass right on by), and plug the superclass pointer of SUPCL to
  1012. \ point to the current n-way for the superclasses of the class we're
  1013. \ defining.
  1014.  
  1015.    0    value    (^SELF)
  1016.    
  1017. : ^SELF  ['] (^self)  displace  ;
  1018.  
  1019. create    SUPCL                    \ dummy superclass
  1020.     classCode  here 2 -  w!
  1021.     classMk ,
  1022.     32 reserve                    \ methods links - no methods
  1023.     0,                            \ ivar link - patched at :CLASS time
  1024.     0,                            \ data len, indexed width
  1025.     0,                            \ flags, xdispl-offs
  1026. \ don't need any more!
  1027.  
  1028.  
  1029. \ META is the super class of Object - top of all inheritance
  1030.  
  1031. : META    reveal
  1032.     [                            \ Note, we're still at the cfa
  1033.     drop                        \ Drop the security marker left by colon
  1034.     classCode  here 2 -  w!
  1035.     classMk ,                    \ class marker goes here
  1036.     32 reserve                    \ methods links - no methods
  1037.     0,                            \ ivar link - set to SUPER below
  1038.     0,                            \ data len, indexed width
  1039.     0,                            \ flags, indexing offs
  1040.     0,                            \ super pointer
  1041.  
  1042. \ Now we set up the SELF and SUPER pseudo-ivars.  We set them up exactly
  1043. \ as if they'd been declared as regular ivars in META.  But note we don't
  1044. \ set up any fields past the "offset" field, since they're irrelevant.
  1045.  
  1046. create    SUP                        \ this is so we can tick it at SuperRef below.
  1047.  
  1048.     here                        \ ready for SELF link below
  1049.     hash, SUPER
  1050.     0,                            \ empty link
  1051.     ' supCl  reloc,                \ ^class is dummy supCl (reloc addr reqd)
  1052.     $ FFFE  w,                    \ "offset" FFFE means SUPER
  1053.  
  1054.  
  1055.     here
  1056.     hash, SELF
  1057.     swap  displ,                \ link (points to SUPER)
  1058.     0,                            \ ^class (gets patched at :CLASS time)
  1059.     $ FFFF  w,                    \ "offset" FFFF means SELF
  1060.  
  1061.  
  1062. dup    ' (^self)    displ!            \ ^SELF will now return addr of SELF ivar
  1063.     ' meta ifa    displ!
  1064.  
  1065. ' meta    metaAddr reloc!            \ patches so NW_IVSETUP can compare
  1066.                                 \ to decide if the end of the superclass
  1067.                                 \ chain has been reached
  1068.  
  1069.  
  1070.     0    value    THISM
  1071.     0    value    SUPERM
  1072. false    value    1SUPER?
  1073.  
  1074.  
  1075. : :CLASS        immediate
  1076.     ?exec  header  classCode w,
  1077.     here -> ^comp_class
  1078.     0 -> pub/priv  0 -> #1st  0 -> #last
  1079.     false -> rec?  false -> union?  false -> static?
  1080.     307  ;
  1081.  
  1082.  
  1083. : MERGE_INFO  { ^sup ivlen \ ^wid wid prevWid -- dlen }
  1084.     ^sup dlen&xwid  -> wid        \ indexed width of this superclass
  1085.     ^sup ffa 1+ c@ 5 and        \ Merge "general" and "indexed" flags with
  1086.     ^comp_class ffa 1+  cset    \  what we have already
  1087.     wid  0EXIT                    \ If this superclass not indexed, we're done
  1088.     
  1089. \ This class is indexed - we need to check if prev classes were indexed
  1090. \  and make sure the widths are compatible.
  1091.  
  1092.     ^comp_class dfa 2+  -> ^wid    \ Addr of wid field in class we're building
  1093.     ^wid w@  -> prevWid            \ Get previous width
  1094.     wid 32760 u>                \ "indexed width" of 32766/7 really means
  1095.     IF                            \  obj_array.
  1096.         prevWid                    \ In this case if we already have a width,
  1097.         IF        prevWid -> wid    \  we use that,
  1098.         ELSE    wid
  1099.                 ivlen  -> wid    \ otherwise current ivar len becomes the width.
  1100.  
  1101.             ( old wid ) 32766 =
  1102.                 IF        \ large_obj_array - mark boundary between ivars
  1103.                         \  we are/aren't mapping to the indexed area
  1104.                     ivlen aligned  ^comp_class xoffa w!
  1105.                     wid aligned 2+  -> wid    \ and allow for ^class offset
  1106.                                             \  field before each element
  1107.                 THEN
  1108.         THEN
  1109.     THEN
  1110.     prevWid
  1111.     NIF     wid  ^wid w!        \ If no prev width, set width & we're done
  1112.     ELSE    prevWid wid <>  ?error 88        \ "Incompatible indexed widths"
  1113.     THEN
  1114. ;
  1115.  
  1116.  
  1117. local    (SUP)   { \ ivlen ^nway ^sup ^newClass thisLen -- }
  1118.  
  1119. : NEXT_SUPER    ( cfa -- )
  1120.     chkClass  -> ^sup
  1121.     ^sup reloc,                        \ Add ^class to n-way
  1122.     ^sup ivlen merge_info   -> thisLen
  1123.     #sup IF                            \ If this is a subsequent class,
  1124.         ivlen aligned  2+  -> ivlen    \  align and allow for ^class offset
  1125.     THEN
  1126.     thisLen ++> ivlen                \ And add ivar length of new class
  1127.     1 ++> #sup  ;
  1128.  
  1129.  
  1130. : SUPERS_LOOP
  1131.     BEGIN                        \ Loop over superclasses:
  1132.         '                        \ cfa of next item on list
  1133.         }or)? IF  drop  EXIT  THEN
  1134.         ( cfa )  next_super            \ handle next superclass
  1135.         1super?  ?EXIT                \ Yerk has only one superclass
  1136.     AGAIN  ;
  1137.  
  1138.  
  1139. :loc  (SUP)
  1140.     307 ?pairs                        \ Make sure we're in the right place
  1141.     DP -> ^newClass
  1142.     classMk ,  classSize 4- reserve    \ Space for class record
  1143.     DP -> ^nway                        \ n-way for superclasses will
  1144.     0 -> ivlen  0 -> #sup            \  start here
  1145.     ^newClass 4+ 32 bounds
  1146.     DO  ^nway  i displ!  4 +LOOP    \ point methods links to nway
  1147.     ^nway ^newClass IFA  displ!        \ and ivars link
  1148.     false -> relocChk?
  1149.     supers_loop                        \ Loop over superclasses
  1150.     0,                                \ Terminate n-way
  1151.     ['] supCl 4+ 32 bounds
  1152.     DO  ^nway  i displ!  4 +LOOP    \ we point the method and ivar links
  1153.     ^nway  ['] supCl IFA  displ!    \  in supcl to the n-way
  1154.  
  1155.     ^comp_class xoffa w@  ['] supCl xoffa w!    \ and set xoffs in supCl
  1156.  
  1157.     ivlen ^comp_class dfa w!        \ Set total ivar length
  1158.     ^comp_class  ^self 8 +  reloc!    \ Store ^class in SELF
  1159.     true -> relocChk?
  1160.     postpone ]c                        \ In a class definition
  1161.     308
  1162. ;loc
  1163.  
  1164.  
  1165. : SUPER{        false -> 1super?   (sup)  ;        immediate
  1166. : SUPER(        postpone super{  ;                immediate
  1167.  
  1168. : <SUPER    true -> 1super?  (sup)    ;            immediate
  1169.             \ For compatibility with Yerk -- only looks for 1 superclass
  1170.             
  1171.             
  1172. : (;CL)
  1173.     postpone [   postpone c[
  1174.     0 ^self 8 + !  ;
  1175.  
  1176.  
  1177. : ;CLASS
  1178.     (;cl)  308 ?defn  ;            immediate
  1179.  
  1180.  
  1181.    1    value    DFRSELID    \ 1 means no late bind going on - otherwise it's
  1182.                                \  the selector we're late binding with
  1183. true    value    SLCTRS?        \ Set false to treat selectors as normal words
  1184.                             \  for full ANSI compatibility
  1185.  
  1186. : SEL?        \ ( addr -- addr b )  True if word at addr is a selector xxx:
  1187.     slctrs?  NIF  false  EXIT  THEN
  1188.     dup  count tuck  1- + c@  & :  =
  1189.     swap 1 >  and  ;
  1190.  
  1191.  
  1192. : GETSELECT            \ Gets a selector from the input stream
  1193.     mword
  1194.     sel?  not ?error 124
  1195.     hash
  1196.     1 -> dfrSelID  ;
  1197.  
  1198.  
  1199. ' null    vect    GET1ST&LAST
  1200. ' null    vect    DoCall1ST
  1201. ' null    vect    DoCallLast
  1202.  
  1203.  
  1204. : M_HEADER  { selID -- }    \ Builds a method header and entry sequence.
  1205.                             \ Note: also called from the assembler.
  1206.     selID ^comp_class MFA  selID  hashed-hdr    \ Build header
  1207.     drop                            \ drop extra selID (needed by MFA)
  1208.     pub/priv -1 =  1 and  w,        \ public/private flag (default is public)
  1209.     here -> thisM                    \ Remember method cfa
  1210.     Mentry  ;                        \ Compile the entry sequence
  1211.  
  1212.  
  1213.  
  1214. : :M { \ selID -- }     immediate        \ Start compiling a method.
  1215.     true -> method?                    \ Used by Handlers
  1216.     ?class  305
  1217.     rec? ?error 191                    \ unmatched '{' in ivar list
  1218.     0 -> superM
  1219.     getSelect -> selID
  1220.     10 -> cstate                    \ Means we've read :m, no call_1st yet
  1221.     selID ^comp_class MFA_offset true (findm)        \ is method already defined?
  1222.     IF
  1223.         -> superM
  1224.         warnings?
  1225.         IF    cr  0 -> out
  1226.             here count type type# 182         \ "Method redefined"
  1227.         THEN
  1228.         heldMod 
  1229.         NIF  superM ^comp_class > ?error 183  THEN
  1230.                                             \ - but if in same class, error
  1231.         drop
  1232.     THEN
  1233.     get1st&last  ?unHoldMod
  1234.     selID m_header                    \ Build method header
  1235.     #1st #last + IF  thisM 1- 7 bset  THEN
  1236.     $ 42 -> obj_base                \ $ 42 means reg A2 - our obj base
  1237.     0 -> obj_displ                    \ For any inline method calls
  1238.     (:)                                \ Start to compile the method
  1239.     doCall1st  ;                    \ Compile any Call1st calls first
  1240.  
  1241.  
  1242. : ;M
  1243.     (;)
  1244.     #last  IF  true -> method?  doCallLast  defnEnd  false -> method?  THEN
  1245.     0 -> #1st  0 -> #last
  1246.     305 ?defn  ;        immediate
  1247.  
  1248.  
  1249. \    ============== Local sections for methods ==============
  1250.  
  1251. \ These function just like regular local sections.  The implementation
  1252. \ is nearly the same.
  1253.  
  1254.     0    value    MLOC_ADDR
  1255.  
  1256.  
  1257. : MLOCAL        \ Starts a local section for methods
  1258.     local?  ?error 93  1 -> local?        \ We change it to the normal -1
  1259.                                         \ as soon as "{" is read.
  1260.     true -> localSect?
  1261.     postpone :m
  1262.     postpone [
  1263.     here -> mloc_addr  10 allot        \ Like a forward definition.  We
  1264.                                     \ save the addr to patch and leave
  1265.                                     \ room for the JMP instrn which will
  1266.                                     \ be planted by (patch) below.
  1267.     private  ;
  1268.  
  1269.  
  1270. : :MLOC        immediate
  1271.     public  ?loc  getSelect drop  95
  1272.     here  mloc_addr  (patch)    \ Like :F
  1273.     #PL  IF  PLentry  THEN
  1274.     false -> local?                \ We do this here so any EXITs
  1275.                                 \  tidy everything up properly
  1276.     postpone ]  ;
  1277.  
  1278.  
  1279. : ;MLOC        immediate
  1280.     (;)  95 ?pairs                \ As local? is now false, everything else
  1281.     305 ?defn                    \ gets tidied up by (;)
  1282.     false -> localSect?
  1283. ;
  1284.  
  1285.  
  1286. \    ================   INDEXED, GENERAL etc.   =================
  1287.  
  1288. \ These are words which can appear in a class declaration, in the
  1289. \ position
  1290.  
  1291. \  :class someClass super{ someSuper }   general
  1292.  
  1293. \ They add attributes to the class.
  1294.  
  1295.  
  1296. : INDEXED        \ ( width -- )  Sets a class and its subclasses to indexed
  1297.     ?class  ^comp_class dfa 2+  w!  ;
  1298.  
  1299. : LARGE        \ Sets the "large" option on an indexed class, allowing
  1300.             \ the number of elements to be greater than 32K.
  1301.  
  1302.     ?class  ^comp_class ffa 1+  0 bset  ;
  1303.  
  1304.  
  1305. : GENERAL
  1306.  
  1307. (* Sets the "general" option on a class, which will force an ivar of that class
  1308.    to be a general object with a class pointer (so it can be late-bound to) even
  1309.    if it's within a record.  Normally you should just not put such ivars in a
  1310.    record, but using GENERAL gives a bit of extra security, for classes for which
  1311.    you know that they will definitely be late-bound to.  (An attempt to late-bind
  1312.    to an ivar without a class pointer will give the "not an object" error at run
  1313.    time, which isn't easy to track down.)
  1314.    Note that indexed classes are always general anyway.  Also if there's a message
  1315.    sent to [self] somewhere in one of the methods, we know that the class *must*
  1316.    be general, so in this case we simply set the general attribute.
  1317. *)
  1318.     ?class  ^comp_class ffa 1+  2 bset  ;
  1319.  
  1320.  
  1321. \                    ===========================
  1322. \                            SELECTORS
  1323. \                    ===========================
  1324.  
  1325. \ First, here are the special-purpose things which can follow a selector.
  1326. \ These can't appear in isolation.
  1327.  
  1328. \ We allow ** and [] as synonyms of [ ] to late-bind to whatever is on the
  1329. \ stack.  Note:  [] is used in JForth.
  1330.  
  1331. \ We also allow [self] as a synonym of [ self ]
  1332.  
  1333. : **        83 die  ;        \ "Has no meaning unless preceded by a selector"
  1334. : []        83 die  ;
  1335. : [SELF]    83 die  ;
  1336. : SUPER>    83 die  ;
  1337. : IVAR>        83 die  ;
  1338. : CLASS_AS>    83 die    ;
  1339.  
  1340.  
  1341. : ]
  1342.     hide  dfrSelID  1 = IF   postpone ]  EXIT  THEN        \ if no late bind, this is a
  1343.                                                         \  standard Forth ]
  1344.     dfrSelID NIF  187 die  THEN            \ late bound pubilc ivar reference
  1345.                                          \  not implemented yet!
  1346.     251 ?pairs
  1347.     state
  1348.     IF        postpone (defer)  dfrSelID ,
  1349.     ELSE    dfrSelID  send
  1350.     THEN
  1351.     1 -> dfrSelID  ;        immediate
  1352.  
  1353.  
  1354. 100        constant    pubIvarTyp        \ &&& temp
  1355. false    value        need_class?
  1356.  
  1357. false    value        implicit_late_bind?        \ true for pre-2.7 auto-late-bind
  1358.                                             \  to locals or values
  1359.  
  1360.  
  1361. (* REFTOKEN  ( -- <various> type )
  1362.    is called when we've parsed a selector - it determines the type of the
  1363.    following word.
  1364.    
  1365.    The order of checking determines the priority of names.  Up to 2.6 we
  1366.    checked for locals first, but this was a bad idea since a local could
  1367.    have the same name as an object, and implicit late binding to locals
  1368.    was legal.  This wouldn't show up until a crash at run time.  So now we
  1369.    check for temp objects, then ivars, then locals, IF implicit_late_bind?
  1370.    is true.
  1371.  
  1372.    <various> will be the cfa of whatever came after the selector, or
  1373.    ( ^ivar offs xdispl-offs ) for ivars and temp objects (which are treated as ivars
  1374.    of the class Dummy).
  1375. *)
  1376.  
  1377. : REFTOKEN        \ ( -- <various> type )
  1378.  
  1379.     false -> need_class?
  1380.     Mword                                    \ grab next word
  1381.     TOfind    IF  tmpObjTyp    EXIT  THEN        \ check for temp object
  1382.     IVfind    IF  ivarTyp        EXIT  THEN        \ check for ivar
  1383.     
  1384.     implicit_late_bind?
  1385.     IF    Pfind    IF  locTyp    EXIT  THEN        \ check for named parm/locals
  1386.     THEN
  1387.  
  1388.     ( here )  dup thread dup @ +  (find)  0=  ?error 125
  1389.     dup ['] **            =  IF  lbTyp                            EXIT  THEN
  1390.     dup ['] []            =  IF  lbTyp                            EXIT  THEN
  1391.     dup ['] [            =  IF  bktTyp                            EXIT  THEN
  1392.     dup ['] [self]        =  IF  lbSelfTyp                        EXIT  THEN
  1393.     dup ['] super>        =  IF  superTyp                            EXIT  THEN
  1394.     dup ['] ivar>        =  IF  pubIvarTyp                        EXIT  THEN
  1395.     dup ['] class_as>    =  IF  true -> need_class?  classTyp    EXIT  THEN
  1396.     dup hdlr
  1397.     CASE
  1398.         objCode        OF    >obj  objTyp    ENDOF
  1399.         classCode    OF    classTyp        ENDOF
  1400.         -90            OF    classTyp        ENDOF        \ Exported class
  1401.         objPtrCode    OF    objPtrTyp        ENDOF
  1402.         valCode        OF    valTyp            ENDOF
  1403.         wordCode    OF    wordTyp            ENDOF
  1404.         vectCode    OF    wordTyp            ENDOF
  1405.                                 \ Note: here we can treat vectors as words.
  1406.  
  1407.         126 die                        \ "Not an object name"
  1408.     ENDCASE
  1409.  
  1410. \ but if we got wordTyp or valTyp, it's only legal if implicit_late_bind?
  1411. \  is true
  1412.     implicit_late_bind?  ?EXIT        \ all OK - done
  1413.     dup wordTyp =  over valTyp =  or
  1414.     IF  126 die  THEN
  1415. ;
  1416.     
  1417.  
  1418.  
  1419. \ These words handle the binding of a selector to whatever follows it.
  1420.  
  1421. (*    FIX_PIVAR does the housekeeping for accessing a public ivar.  When we
  1422.     encounter  msg: ivar>  then we store the selector in pivSel, and the
  1423.     hashed ivar name in pivar.  We then continue with a zero "selector",
  1424.     which signals that it's a public ivar access, and leads to us being
  1425.     called back here to fix everything up once we've got the class in which
  1426.     the ivar lives.
  1427. *)
  1428.  
  1429. : FIX_PIVAR  { ^class in_class? \ ^ivar offs xdispl-offs -- cfa offs xdispl-offs }
  1430.  
  1431.     ^class ?>classInMod -> ^class
  1432.  
  1433.     pivar ^class <findIV>            \ ( ^ivar offs xdispl-offs true  OR  false )
  1434.     0= ?error 192                    \ "ivar not found"
  1435.     -> xdispl-offs  -> offs  -> ^ivar
  1436.     ^ivar iffa w@                     \ get ivar flags
  1437.     dup 4 and 0=    ?error 193        \ ivar not public
  1438.     2 and                            \ static flag
  1439.     in_class?
  1440.     IF        0=  ?error 197            \ ivar not static
  1441.     ELSE    ?error 195                \ wrong syntax for public static ivar
  1442.     THEN
  1443.  
  1444. \ now we find the method in the ivar's class
  1445.  
  1446.     pivSel ^ivar  ivFindm drop        \ %%% don't worry about large_obj_arrays
  1447.                                     \  which are ivars yet!
  1448.   ( cfa  offs-within-ivar )
  1449.     in_class?
  1450.     IF            \ for public static ivars, the "offset" we return is
  1451.                 \  actually the ivar's real data address.
  1452.         drop ^ivar static_ivar_offs +  -> offs
  1453.     ELSE
  1454.         ++> offs
  1455.      THEN
  1456.      offs  xdispl-offs
  1457. ;
  1458.  
  1459.  
  1460. \ PUBLIC_STATIC_IVAR_REF handles a message bind to a public static ivar
  1461. \ (done via the  msg: ivar> in_class someClass  syntax)
  1462.  
  1463. : PUBLIC_STATIC_IVAR_REF
  1464.     refToken
  1465.     classTyp <>  ?error 196            \ class name must follow in_class
  1466.     true  fix_pivar drop            \ %%% don't worry about large_obj_arrays
  1467.                                     \  which are public static ivars yet!
  1468.     0  bind_to_obj
  1469. ;
  1470.  
  1471.  
  1472. \ OBJREF handles a reference to a normal object.
  1473.  
  1474. : OBJREF  { selID ^obj \ cfa offs xdispl-offs -- }
  1475.  
  1476.     selID
  1477.     IF    selID ^obj  objFindm
  1478.     ELSE                \ it's a public ivar reference in the referenced object
  1479.         ^obj >class  false  fix_pivar
  1480.     THEN
  1481.  
  1482.   ( cfa offs xdispl-offs )  -> xdispl-offs  -> offs  -> cfa
  1483.     xdispl-offs
  1484.     IF    ^obj xdispl-offs +  lit-addr
  1485.         postpone dup postpone @ postpone +
  1486.         offs IF  offs postpone literal  postpone +  THEN    \ will normally be zero
  1487.         cfa bind_to_stk  EXIT
  1488.     THEN
  1489.  
  1490.      cfa ^obj offs bind_to_obj
  1491. ;
  1492.  
  1493.  
  1494. \ IVARREF handles a reference to an ivar.
  1495.  
  1496. : IVARREF  { selID ^ivar offs xdispl-offs \ cfa stat? -- }
  1497.  
  1498.     heldMod  0 -> heldMod                \ save
  1499.     offs  $ FFFE >=  -> selfRef?        \ if self or super.  Allows private
  1500.                                         \ methods to be found by (findm)
  1501.     selfRef?
  1502.     IF  supers_to_skip -> sups2skip        \ sups2skip is interrogated by (findm).
  1503.                                         \  This must only be done if self or
  1504.                                         \  super is the target.
  1505.         0 -> offs                        \ "real" offset is zero
  1506.     ELSE
  1507.         ^ivar iffa w@ 2 and  -> stat?    \ static ivar?
  1508.     THEN
  1509.  
  1510.     selID
  1511.     IF    selID ^ivar ivFindM            \ %%% don't worry about large_obj_arrays
  1512.                                     \  which are ivars yet!
  1513.         selfRef? IF -> xdispl-offs  ELSE drop THEN
  1514.  
  1515.         ++> offs                    \ add embedded obj base offs to ivar offs
  1516.         -> cfa
  1517.         0 -> sups2skip  0 -> supers_to_skip
  1518.  
  1519.         selfRef?
  1520.         IF    xdispl-offs
  1521.             IF    postpone ^base  xdispl-offs postpone literal  postpone +
  1522.                 postpone dup postpone @ postpone +
  1523.                 cfa  bind_to_stk
  1524.             ELSE
  1525.                 cfa offs bind_to_self  false -> selfRef?
  1526.             THEN
  1527.             ?unholdMod  -> heldMod   EXIT
  1528.         THEN
  1529.  
  1530.     ELSE            \ it's a public ivar reference within the referenced ivar
  1531.         ^ivar ^iclass false  fix_pivar drop        \ %%% don't worry about large_obj_arrays
  1532.                                                 \  which are ivars yet!
  1533.         ++> offs  -> cfa
  1534.     THEN
  1535.  
  1536.     stat?
  1537.     IF    cfa ^ivar static_ivar_offs  bind_to_obj
  1538.         ?unholdMod  -> heldMod  EXIT
  1539.     THEN
  1540.     
  1541.     xdispl-offs
  1542.     IF    postpone ^base  xdispl-offs postpone literal  postpone +
  1543.         postpone dup postpone @ postpone +
  1544.         offs IF  offs postpone literal  postpone +  THEN    \ will normally be zero
  1545.         cfa  bind_to_stk
  1546.     ELSE
  1547.         cfa offs  bind_to_ivar
  1548.     THEN
  1549.     ?unholdMod  -> heldMod
  1550. ;
  1551.  
  1552.  
  1553. \ OP/CL is common code factored out of objPtrRef and classRef, which
  1554. \ are very similar.
  1555.  
  1556. : OP/CL  { selID ^class \ cfa offs xdispl-offs -- }
  1557.     selID
  1558.     IF    selID ^class clFindm
  1559.     ELSE
  1560.         ^class  false  fix_pivar
  1561.     THEN
  1562.     -> xdispl-offs  -> offs  -> cfa
  1563.  
  1564.     xdispl-offs
  1565.     IF    xdispl-offs postpone literal  postpone +
  1566.         postpone dup postpone @ postpone +
  1567.     THEN
  1568.     
  1569.     offs postpone literal  postpone +
  1570.     cfa bind_to_stk
  1571. ;
  1572.  
  1573.  
  1574. \ OBJPTRREF handles a reference to an object pointer.
  1575.  
  1576. : OBJPTRREF  { selID OP-cfa \ ^class cfa offs xdispl-offs -- }
  1577.     OP-cfa (comp)                    \ Compile a fetch of the OP-cfa,
  1578.                                     \  giving ^obj at run time
  1579.     OP-cfa 4+ @  0= ?error 86        \ "ObjPtr hasn't had a class specified"
  1580.     OP-cfa 4+ @abs  -> ^class
  1581.     ^class hdlr -90 =
  1582.     IF                                \ Class is exported
  1583.         ^class 6 + wdisplace        \ Addr of module
  1584.         compmod =  ?error 84        \ It's the module we're compiling -
  1585.                                     \  this is a no-no, since the ObjPtr
  1586.                                     \  reference will use the OLD module!
  1587.         ^class  ?>classInMod -> ^class
  1588.     THEN
  1589.     selID ^class  OP/cl
  1590. ;
  1591.  
  1592.  
  1593. \ CLASSREF handles a reference to a class - this means use the object
  1594. \  whose addr is on the stack, but ASSUME it is of the given class
  1595. \  and early bind, without checking.
  1596. \ The code is very similar to objPtrRef, naturally enough.
  1597.  
  1598. : CLASSREF { selID ^class \ cfa offs xdispl-offs -- }
  1599.     need_class? IF  '  chkClass  -> ^class  false -> need_class?  THEN
  1600.     selID ^class  OP/cl
  1601. ;
  1602.  
  1603.  
  1604. \ TMPOBJREF handles a reference to a temp object.
  1605.  
  1606. : TMPOBJREF  { selID ^tmpObj offs \ svHeldMod cfa xdispl-offs -- }
  1607.  
  1608.     heldMod -> svHeldMod  0 -> heldMod
  1609.     selID
  1610.     IF    selID ^tmpObj ivFindM
  1611.     ELSE
  1612.         ^tmpObj 8 + @abs  false  fix_pivar
  1613.     THEN
  1614.     -> xdispl-offs  ++> offs  -> cfa
  1615.  
  1616.     xdispl-offs
  1617.     IF    postpone locReg
  1618.         xdispl-offs postpone literal  postpone +
  1619.         postpone dup postpone @ postpone +
  1620.         offs IF  offs postpone literal  postpone +  THEN    \ will normally be zero
  1621.         cfa  bind_to_stk
  1622.     ELSE
  1623.          cfa offs  bind_to_tmpObj
  1624.         svHeldMod -> heldMod
  1625.     THEN
  1626. ;
  1627.  
  1628.  
  1629. \ SuperRef handles the  msg: super> someSuper  construct.
  1630.  
  1631. : SUPERREF { selID \ ^nway namedClass ^nway' cnt -- }
  1632.     ?class                            \ Must be compiling a class
  1633.     '  -> namedClass                \ get named class xt
  1634.     ^comp_class sfa -> ^nway
  1635.     ^nway -> ^nway'  0 -> cnt
  1636.     BEGIN
  1637.         ^nway' @ 0= ?error 120            \ "superclass" not found
  1638.         ^nway' @abs namedClass =
  1639.     NWHILE
  1640.         1cell ++> ^nway'  1 ++> cnt
  1641.     REPEAT
  1642.     cnt -> supers_to_skip
  1643.     selID  ['] sup  $ FFFE  0  ivarRef        \ equivalent to msg: super
  1644. ;
  1645.  
  1646. forward COMPREF
  1647.  
  1648. \ PubIvarRef handles the  msg: ivar> someIvar IN someObj  construct, to
  1649. \  send a message directly to a public ivar in an object.  At this point
  1650. \  we've just read "ivar>".
  1651.  
  1652. : PUBIVARREF  { selID \ addr len ^class ^ivar -- }
  1653.     selID -> pivSel                    \ save selID being sent to the ivar
  1654.     mword hash  -> pivar            \ parse ivar name
  1655.     mword count  -> len  -> addr
  1656.     addr len  " IN" s=
  1657.     IF    0                 \ dummy "selID" for compRef (not a legal selector)
  1658.         compRef            \ handle whatever object comes after IN.  The
  1659.                         \  zero selector signals that a public ivar in the
  1660.                         \  indicated object is to be accessed - real selectors
  1661.                         \  can't ever be zero.  This will lead to fix_pivar
  1662.                         \  being called to complete the job.
  1663.     ELSE
  1664.         addr len " IN_CLASS" s=
  1665.         IF        public_static_ivar_ref
  1666.         ELSE    true ?error 194        \ "wrong syntax for public ivar"
  1667.         THEN
  1668.     THEN
  1669. ;
  1670.  
  1671.  
  1672. \ LBselfRef handles messages to [self] - i.e. late bound to Self.
  1673.  
  1674. : LBSELFREF
  1675.     postpone self  postpone (defer)  ,
  1676. ;
  1677.  
  1678. \ Since any class with a late-bound message to self MUST be general, we
  1679. \  used to force it to general at this point.  But since class Object
  1680. \  now has a call to [self] in deep_classinit:, this got us rapidly
  1681. \  into crash territory!  So just remember the general when it's needed.
  1682.  
  1683.  
  1684. : COMPDFR    \ (selID cfa -- )
  1685.     (comp)  postpone (defer)  ,  ;
  1686.  
  1687.  
  1688. \ Now here are the main words which compile the selector bindings.
  1689.  
  1690. \ CompRef operates at compile time - it compiles a selector bind.
  1691.  
  1692. :f COMPREF        \ ( selID -- )
  1693.  
  1694.     refToken    \ ( selID <various> type )
  1695.                 \    <various> will be the cfa of whatever came after the selector,
  1696.                 \    or ( offset ^ivar ) for ivars and temp objects (which are
  1697.                 \    treated as ivars of the class Dummy).
  1698.  
  1699.     CASE
  1700.         objTyp        OF  objRef                            ENDOF
  1701.         ivarTyp        OF    ivarRef                            ENDOF
  1702.         objPtrTyp    OF  objPtrRef                        ENDOF
  1703.         tmpObjTyp    OF  tmpObjRef                        ENDOF
  1704.         classTyp    OF    classRef                        ENDOF
  1705.  
  1706. \ These next 3 can only come up if implicit_late_bind? is true:
  1707.         valTyp        OF  compdfr                            ENDOF
  1708.         locTyp        OF  compdfr                            ENDOF
  1709.         wordTyp        OF  compdfr                            ENDOF
  1710.  
  1711.         lbTyp        OF  drop  postpone (defer)  ,        ENDOF
  1712.         lbSelfTyp    OF  drop  LBselfRef                    ENDOF
  1713.         bktTyp        OF  drop  -> dfrSelID  251            ENDOF
  1714.         superTyp    OF    drop  superRef                    ENDOF
  1715.         pubIvarTyp    OF    drop  pubIvarRef                ENDOF
  1716.  
  1717.         82 die                        \ "Selector can't be used on that"
  1718.         
  1719.     ENDCASE  ;f
  1720.  
  1721.  
  1722. (*
  1723. RunRef is the execution mode equivalent - it executes a selector bind.
  1724. We do this simply by compiling it in a buffer then executing it there.
  1725. This replaces the earlier scheme where we had to separately handle each
  1726. case as for compRef - this was a Neon carryover.
  1727.  
  1728. While we're compiling in the buffer, we save the DP on the return stack,
  1729. then restore it before executing what we compiled (since it might do some
  1730. compiling itself).  This isn't long, but it's a bit tricky:
  1731. *)
  1732.  
  1733.     variable    runRefBuf    56 reserve    \ allows 4 nested binds - worst case
  1734.                                         \  14 bytes each
  1735. 0    value        bufPtr
  1736. 0    value        hiDP
  1737.  
  1738. : RUNREF  { selID \ svDP svBufPtr svState -- }
  1739.     DP -> svDP                \ save DP
  1740.     DP hiDP umax -> hiDP    \ so we can reset DP to right place on an error
  1741.  
  1742.     bufPtr NIF  runRefBuf  ELSE  bufPtr  THEN
  1743.     dup -> DP  -> svBufPtr    \ now we'll compile in runRefBuf
  1744.     state -> svState        \ save state
  1745.     postpone ]            \ need compile state so this compilation works properly
  1746.     selID compRef        \ compile the binding
  1747.     postpone (exit)        \ and an exit, so we return to interpretation
  1748.     svState -> state    \ restore state
  1749.     0 -> hiDP            \ don't need it any more and could cause problems
  1750.     ?unholdMod
  1751.     DP -> bufPtr        \ new bufPtr value
  1752.     svDP -> DP            \ restore DP since the code might compile something
  1753.     patches_done        \ we're about to execute what we just compiled
  1754.     svBufPtr execute    \ execute at old bufPtr location
  1755.     svBufPtr -> bufPtr    \ then restore old bufPtr
  1756. ;
  1757.  
  1758.  
  1759. \                ======== Selector support =========
  1760.  
  1761.  
  1762. \ MESSAGE is the handling word invoked by using a selector.
  1763.  
  1764. : MESSAGE        immediate
  1765.     state
  1766.     IF                      \ Compile state
  1767.         compRef                \ Compile the message send
  1768.         ?unHoldMod
  1769.     ELSE
  1770.         runRef                \ Run state - execute object/vector reference.
  1771.                             \ ?unHoldMod is called by ex-method at the
  1772.                             \ end, so we don't need to call it here.
  1773.     THEN  ;
  1774.  
  1775.  
  1776. \ 1stFind lumps together all the special cases we have to look for after
  1777. \ we've parsed an input word, but before we can do a regular dictionary
  1778. \ lookup.  At present these are selectors, named parms/locals, ivars
  1779. \ and local objects.  If we invent more later, they can easily be added.
  1780. \ The vector Ufind is then set to this word so it is called before the
  1781. \ regular dictionary search.  If we succeed here, we return the selector
  1782. \ ID or zero, the cfa of the handling word, and 1 or -1 (this will cause
  1783. \ FIND to exit without doing anything more).  If we fail, we return the
  1784. \ original string address and false.
  1785.  
  1786. : 1stFIND    \ ( str-addr -- selID message-cfa T  |  -- str-addr F )
  1787.     sel?                        \ is it a selector?
  1788.     IF        hash                \ yes - leave selID
  1789.             ['] message  1        \  and cfa of message, and 1 (it's immediate)
  1790.     ELSE    LocFind                \ no - look for the various kinds of local name
  1791.     THEN  ;
  1792.  
  1793.  
  1794. ' 1stFind -> Ufind
  1795.  
  1796. getSelect classinit:  -> initID
  1797.  
  1798.  
  1799. forward DUMP
  1800.  
  1801.  
  1802. \ SET_CLASS is a utility word used to patch nucleus objects when their classes
  1803. \ are defined in higher-level files.  Actually it could be used to change the
  1804. \ class of any object, if anyone is silly enough to want to do that.
  1805.  
  1806. \ Usage:  fFcb  ['] file  set_class
  1807.  
  1808. : SET_CLASS  { ^obj theClass -- }
  1809.     theClass  chkClass  ^obj 6 -  reloc!        \ Patch ^class
  1810.     6  ^obj 8 -  w!                    \ Not indexed (yet)
  1811.     -6 ^obj  2-  w!  ;                \ ^class offset
  1812.  
  1813.  
  1814. : CHKSAME        \ ( ^obj -- ^obj )
  1815.         \ A check that two objects are of exactly the
  1816.         \ same class.
  1817.     dup >classXt  ^base >classXt  <> ?error 87  ;
  1818.  
  1819.  
  1820. \            ========= Object pointers ==========
  1821.  
  1822. \ Object pointers are low-level objects (like VALUEs) which point to a
  1823. \ normal (high-level) object, and which allow early-bound messages to be
  1824. \ sent to the object by syntactically sending them to the object pointer.
  1825.  
  1826. \ The normal syntax is
  1827.  
  1828. \  ObjPtr  ZZZ    class_is  someClass
  1829.  
  1830. \ Thereafter, any messages sent to zzz are early-bound to the object that
  1831. \ zzz points to at the time the message executes.
  1832.  
  1833. \ If you need to declare the object pointer before the class exists, use
  1834. \ SET_TO_CLASS once the class is defined, thus:
  1835. \
  1836. \ :class  SOMECLASS    super{ object }
  1837. \
  1838. \    ' someOP  set_to_class  someClass
  1839. \
  1840. \    etc.
  1841.  
  1842.  
  1843. true    value    check_OP_stores?    \ allows us to turn off type checking
  1844.                                     \  for stores to objPtrs
  1845.  
  1846. : (ToOP)  { ^obj OPcfa \ OPcl -- }
  1847.  
  1848.     ^obj  nilP =                \ If we're storing nil, anything goes
  1849.     check_OP_stores? not or        \ Or if checking is turned off
  1850.     NIF
  1851.         OPcfa 4+ @abs  -> OPcl
  1852.         ^obj 6 - @abs  OPcl  <>
  1853.         IF                      \ Mismatch. We give some useful(?) info.
  1854.             cr  ^obj obj> .id ."  -> "  OPcfa .id
  1855.             87 die
  1856.         THEN
  1857.     THEN
  1858.     ^obj OPcfa !  ;
  1859.  
  1860.  
  1861. :f  ToObjPtr
  1862.     state
  1863.     IF  lit-addr  postpone (toOP)  ELSE  (toOP)  THEN  ;f
  1864.  
  1865.  
  1866. : CLASS_IS    \ ( --< class > )
  1867.     ?exec  '  chkClass  here 4-  reloc!  ;
  1868.  
  1869.  
  1870. : SET_TO_CLASS  { ^objPtr \ ^cl --< class > }
  1871.     '  -> ^cl
  1872.     ^objPtr hdlr -62 <> ?error 85        \ "That isn't an ObjPtr"
  1873.  
  1874.             \ Now if "class" is an imported word, we change the handler code
  1875.             \ to "imported class".  This is normally done when the module
  1876.             \ is compiled, but it may not be yet, since we probably
  1877.             \ want to refer to the ObjPtr in the module.
  1878.  
  1879.     ^cl hdlr -92 = IF  -90 ^cl 2- w!  ELSE  ^cl chkClass drop  THEN
  1880.     ^cl  ^objPtr 4+  reloc!  ;
  1881.  
  1882.  
  1883. \ If you are late-binding in a loop, it can be much faster if you do the bind
  1884. \ just once, then reuse the resulting cfa each time in the loop.  This way
  1885. \ you only have to perform the method search once.  To bind initially and get
  1886. \ the cfa, use
  1887.  
  1888. \  BIND_WITH ( ^obj --<selector> ^obj-modified  cfa )
  1889.  
  1890. \ Usage:  (saveCfa and ^obj-mod are values or locals)
  1891.  
  1892. \    (get object's address)  bind_with someSelector:  -> saveCfa  -> ^obj-mod
  1893.  
  1894. \    (in the loop)  ^obj-mod  saveCfa  ex-method
  1895.  
  1896. \ The use of the modified object address is a bit obscure, and is related to
  1897. \ multiple inheritance.  The method you actually end up binding to may be in
  1898. \ one of the superclasses, and the ivars for that superclass may not start at
  1899. \ the beginning of the object.  The modified object address is the start of
  1900. \ the ivars for the superclass, which is the address the method needs.
  1901.  
  1902. \ Note also that the method may turn out to be in a module, so when you have
  1903. \ finished you should put ?unHoldMod to free up the module.
  1904.  
  1905. : (BWITH)  { ^obj selID \ cfa offs -- ^obj-modified  cfa }
  1906.     selID ^obj ?>class  clFindm
  1907.     drop ( %%%% )
  1908.     -> offs  -> cfa
  1909.     ^obj offs +  cfa  ;
  1910.  
  1911.  
  1912. : BIND_WITH        \ ( ^obj --<selector> ^obj-modified  cfa )
  1913.     getSelect  postpone literal
  1914.     postpone (bwith)  ;        immediate
  1915.  
  1916.  
  1917. \        ===================================
  1918.  
  1919. :class    OBJECT    super{ meta }
  1920.  
  1921. :m CLASS:    ^base ?>class ?>classinMod  ;m
  1922.  
  1923. :m .ID:        ^base obj>  .id  ;m
  1924.  
  1925. :m .CLASS:    ^base >classXt  .id  ;m
  1926.  
  1927. :m ADDR:    inline{ ^base}  ;m
  1928.  
  1929. \ :m ABS:        ^base  ;m        \ Obsolete
  1930.  
  1931. :m LENGTH:    \ ( -- len )  Gets total length of object.
  1932.     objlen  ;m
  1933.  
  1934.  
  1935. (*    Here are two methods which operate between this object and another of
  1936.     the same class.  Note we don't check that the passed-in object is actually
  1937.     of the same class, since it could be a subclass but still be safe to use
  1938.     here.
  1939. *)
  1940.  
  1941. :m COPYTO:    \ ( ^obj -- )  Copies the ivar part of the passed-in object
  1942.             \ to self.
  1943.     ^base  dup (^dlen) w@  aligned_move  ;m
  1944.  
  1945. :m =?:        \ ( ^obj -- b )  Returns true if the ivar part of the passed-in
  1946.             \ object is identical to self.
  1947.     ^base  dup (^dlen) w@  (s=)  ;m
  1948.  
  1949.  
  1950. (*    The following methods need to be defined for all objects.
  1951.     We give them their default definitions here.
  1952. *)
  1953.  
  1954. :m CLASSINIT:  ;m    \ Our standard constructor method.  Called automatically
  1955.                     \ whenever an object is created.
  1956.  
  1957.  
  1958. :m DEEP_CLASSINIT:    \ Also does classinit: on all nested ivars.  Use for
  1959.                     \  totally (re-)initializing an object.
  1960.     ^base -> newObject
  1961.     class: self ifa displace  0  0
  1962.     ivSetup
  1963. \    classinit: [self]
  1964.     ?unholdMod
  1965. ;m
  1966.  
  1967.  
  1968. (*    RELEASE: is our standard destructor method.  Any objects that
  1969.     allocate heap storage will redefine this appropriately.
  1970.     Our convention is that an object will release ALL its
  1971.     storage when it gets a release: message. Other methods
  1972.     can be provided to partly release storage, as needed.
  1973. *)
  1974.  
  1975. :m RELEASE:    inline{ }  ;m
  1976.  
  1977.  
  1978. (*    SEND: and BRING: handle serialization of an object, so
  1979.     it can be saved to a file or whatever.  We take a
  1980.     passed-in object as the source/sink for the serialized
  1981.     bytes.  It can be any object that supports the stream 
  1982.     methods read: and write:.
  1983.  
  1984.     Here in class Object we just assume we can just write
  1985.     the object's local data.  Any classes that use handles 
  1986.     etc. will have to do a bit more than this.
  1987.     
  1988.     We write the non-indexed and indexed data separately, 
  1989.     to meke these operations less sensitive to platform-related
  1990.     alignment questions.  On the PPC the indexed area
  1991.     starts out 4-byte aligned, but only 2-byte aligned
  1992.     on the 68k.  Of course alignment issues within the
  1993.     local ivars might rule out cross-platform compatibility
  1994.     anyway, but there will be many situations in which
  1995.     what we do here will work.
  1996. *)
  1997.  
  1998. :m SEND:  { stream \ ^dlen xwid -- }
  1999.  
  2000.     ^base (^dlen)  -> ^dlen
  2001.     ^base
  2002.     ^dlen w@                        \ ivar len
  2003.     write: [ stream ]  OK?            \ write out ivar data
  2004.  
  2005.     ^dlen 2+ w@  dup -> xwid  0EXIT    \ if not indexed, we're done
  2006.     
  2007.     idxBase dup
  2008.     4- @ 1+  xwid *                 \ indexed length
  2009.     write: [ stream ]  OK?            \ write out indexed data
  2010. ;m
  2011.  
  2012. :m BRING:  { stream \ ^dlen xwid -- }
  2013.     ^base (^dlen)  -> ^dlen
  2014.     ^base
  2015.     ^dlen w@                        \ ivar len
  2016.     read: [ stream ]  OK?            \ read ivar data
  2017.  
  2018.     ^dlen 2+ w@  dup -> xwid  0EXIT    \ if not indexed, we're done
  2019.     
  2020.     idxBase dup
  2021.     4- @ 1+  xwid *                 \ indexed length
  2022.     read: [ stream ]  OK?            \ read indexed data
  2023. ;m
  2024.  
  2025.  
  2026. :m DUMP:
  2027.     .id: self  ."  class: "  .class: self
  2028.     ^base  objlen  dump  ;m
  2029.  
  2030. :m PRINT:        \ Used for a formatted display, if appropriate.
  2031.                 \ Default is just a dump.
  2032.     dump: self  ;m
  2033.  
  2034. ;class
  2035.  
  2036.  
  2037. \ Bytes is used as the allocation primitive for basic classes
  2038.  
  2039. : BYTES  { numBytes \ svRec? -- }
  2040.     ?class
  2041.     rec? -> svRec?  true -> rec?    \ Don't want an object header here
  2042.     ['] object ivDef
  2043.     numBytes  ^comp_class dfa  w+!
  2044.     svRec? -> rec?  ;
  2045.  
  2046.  
  2047.  
  2048. (*        ================  Temp (local) objects  ===================
  2049.  
  2050.     Syntax:
  2051.     
  2052.     : aWord  { loc1 loc2 -- }        \ Locals are optional, of course
  2053.         temp
  2054.         {    var        v1
  2055.             int        i1
  2056.             string    s
  2057.         }
  2058.  
  2059.     Or you can use temp{ ...  } if you prefer.
  2060.  
  2061.     As the syntax is quite similar to a list of ivars of a class, we actually
  2062.     implement the temp objects as though they're the ivars of a dummy class
  2063.     (which we uncreatively call Dummy).  This is just a convenience during
  2064.     the compilation of a defn with temp objects.  It allows us to define them
  2065.     and keep them visible during the compilation of the definition, while 
  2066.     being able to mainly use existing code for ivar access.  We don't need 
  2067.     these ivar dic entries once the defn is finished, so we actually put them
  2068.     high in the dictionary out of the way of the defn we're compiling.  At 
  2069.     the end of the defn, we reinitialize Dummy's ivar link ready for next time.
  2070. *)
  2071.  
  2072. getSelect release:        constant    releaseID
  2073.  
  2074.  
  2075. :class DUMMY  super{ object }
  2076. ;class
  2077.  
  2078. ' dummy ifa @    constant    dummyIfa
  2079.             \ ivar link corresponding to no ivars - it will be a relative
  2080.             \  pointer to the n-way for the superclass, and thus a constant
  2081.  
  2082. : RESETTEMPS
  2083.     dummyIfa  ['] dummy ifa  !
  2084.     0  ['] dummy dfa !                \ clear dlen and xwid
  2085. ;
  2086.     
  2087.     \ Note we don't have to worry about the mfa since Dummy never gets
  2088.     \ its own methods.
  2089.  
  2090.  
  2091. (*    InitTemps is called when we're compiling the prolog for a definition
  2092.     with temp objects.  It compiles a call to make_obj for each object, so
  2093.     that they're properly initialized.  Note we can't just call make_obj once
  2094.     using class Dummy, since its ivar list is wiped out after each defn
  2095.     with temp objects, so at run time it won't have any!  But we don't need
  2096.     Dummy at run time anyway - we only need the "ivars" which are the
  2097.     temp objects themselves.
  2098. *)
  2099.  
  2100. : 1TEMP  ( ^iclass ioffs -- )
  2101.     locReg +  make_obj  ;
  2102.     
  2103.  
  2104. :f INITTEMPS  { \ infa ^class -- }
  2105.     ['] dummy ifa displace  -> infa
  2106.     BEGIN
  2107.         infa @ 0<
  2108.     WHILE
  2109.         infa ^iclass -> ^class
  2110.         ^class xwid
  2111.         IF        \ it's indexed - we'll have #elements on the stack,
  2112.                 \  so we need to compile it as a literal for
  2113.                 \  make_obj to grab at run time.
  2114.             infa i#els  postpone literal
  2115.         THEN
  2116.         ^class lit-addr
  2117.         infa ioffs  postpone literal
  2118.         postpone locreg  postpone +  postpone make_obj
  2119.         infa ^nextivar  -> infa
  2120.     REPEAT  ;f
  2121.  
  2122.  
  2123. (*    ReleaseTemps is called back from Handlers when it's compiling an exit.
  2124.     It compiles a release: xxx for all temp objects.  Because of the way
  2125.     we've defined release: in class Object, for simple objects no code will
  2126.     actually be generated.  
  2127.     
  2128.     Note we mustn't call resetTemps here since this might be an EXIT, not
  2129.     the final semicolon.  We leave calling resetTemps till a new temp{ comes
  2130.     up.
  2131. *)
  2132.  
  2133. : RELEASETEMPS  { \ infa -- }
  2134.     ['] dummy ifa displace  -> infa
  2135.     BEGIN
  2136.         infa @ 0<
  2137.     WHILE
  2138.         infa  ^iclass  0EXIT            \ shouldn't happen, actually
  2139.         releaseID  infa  ivFindM 2drop
  2140.         infa ioffs bind_to_tmpObj        \ compile release:
  2141.         infa ^nextivar  -> infa
  2142.     REPEAT
  2143. ;
  2144.  
  2145.  
  2146. : }TEMP
  2147.     130 ?pairs
  2148.     ['] } >body !                        \ restore old action for "}"
  2149.     -> ^comp_class  -> state  -> cstate  -> DP    \ restore other things
  2150.     tmpObjs dlen 8 +  -> frameSize        \ work out frame size
  2151.     local? NIF                            \ compile prolog unless we're in
  2152.         PLentry  initTemps                \  a local section (then it gets done
  2153.     THEN                                \  by :LOC)
  2154.     ['] releaseTemps -> relTmps            \ for Handlers callback at exit time
  2155. ;
  2156.  
  2157.  
  2158. : TEMP{        immediate
  2159.  
  2160. (*    First we have to allocate an internal local variable as a frame pointer.
  2161.     There are 4 situations.  There may or may not already be locals, and
  2162.     we may or may not be in a local section.  Note we can be in a local
  2163.     section even if there aren't already locals, since the purpose of the
  2164.     local section might be just to establish a section for these temp objects.
  2165.  
  2166.     If there are already locals, we just add another.  If we're not in a
  2167.     local section we need to recompile the entry sequence (done by PLentry)
  2168.     since the number of regs to be saved and set up is different.  But if
  2169.     we're in a local section, we don't have to recompile since we haven't
  2170.     called PLentry yet, so we just add the extra local.  If there aren't any
  2171.     locals already, we just call initLocs which sets them up, before adding
  2172.     the new one.
  2173. *)
  2174.     resetTemps
  2175.     #PL IF
  2176.         local?    NIF  PLentry_addr -> DP  THEN
  2177.     ELSE
  2178.         initLocs                \ No locs before, so set up for them now
  2179.     THEN
  2180.     local? IF  -1 -> local?  THEN    \ If in a local section, setting local?
  2181.                                     \ to -1 means we've defined the locals
  2182.                                     \ so can't do it again
  2183.     " x " here place  here addToParmList
  2184.  
  2185. (*    next we save DP and move halfway up in the free dic space - we'll put
  2186.     the "ivar dic entries" for the temp objs there - we don't need them
  2187.     after the defn is compiled.
  2188. *)
  2189.     here            room 2/ ++> DP  align-dp
  2190.     cstate            true -> cstate
  2191.     state
  2192.     ^comp_class
  2193.     ['] } >body @                \ save old action for "}"
  2194.     ['] }temp  -> }                \ "}" will now be same as }temp
  2195.     130                            \ for ?pairs
  2196.  
  2197.     ['] dummy dup    -> ^comp_class    \ local objs will look like ivars of Dummy
  2198.                     -> tmpObjs        \ this will enable finding them
  2199.     
  2200.  
  2201.  
  2202.     postpone [                    \ stop compiling
  2203. ;
  2204.  
  2205.                             
  2206. : TEMP        gobble{  postpone temp{  ;        immediate
  2207.  
  2208.  
  2209. (*    On the PowerPC, a temp object can be specified to be instantiated 
  2210.     in a register if possible, by putting "register" before its 
  2211.     declaration (a bit like C).  We don't do that here on the 68k,
  2212.     but for source code commonality we recognize "register" and
  2213.     ignore it.
  2214. *)
  2215.  
  2216. : REGISTER  ;
  2217.  
  2218.  
  2219. (*        =================  Records and unions  ====================
  2220.  
  2221. Syntax:
  2222.  
  2223.     record <name>        \ The name is optional
  2224.    {    var        v1
  2225.         int        i1
  2226.         string    s
  2227.    }
  2228.    
  2229.        union <name>        \ The name is optional
  2230.    {    var        v1
  2231.         int        i1
  2232.         string    s
  2233.    }
  2234.  
  2235.  
  2236. Or you can use record{ ...  } or union{ ... } if you prefer, if it's
  2237. unnamed.  The similarity of syntax to temp objects is quite deliberate.
  2238. But any similarity to Your Favorite Language is entirely accidental.  Well
  2239. actually it's not, but I think this syntax is as good as any, and probably
  2240. more readable for folks coming from the land of C.
  2241.  
  2242. unions can be nested within records and vice versa.
  2243.  
  2244. NOTE: it's best to not use unions unless you're really sure you know what
  2245. you're doing.  Having different objects sharing the same memory is sure
  2246. to cause problems if you're careless!
  2247.  
  2248. *)
  2249.  
  2250. : SVREC        
  2251.    ^comp_class dfa w@ 
  2252.     rec?  
  2253.     union?  
  2254.     unionOffs 
  2255. ;
  2256.  
  2257. : RSTREC    
  2258.     -> unionOffs  
  2259.     -> union?  
  2260.     -> rec?  
  2261.     union? IF     \ we fell back in a union, so we
  2262.                 \ reset data pointer to were it was at the beginning
  2263.                 \ of this union/rec
  2264.         ^comp_class dfa w!
  2265.     ELSE
  2266.         drop
  2267.     THEN
  2268. ;
  2269.  
  2270. : ?HANDLE_NAME  { \ sv_>in sv_^class sv_rec? -- }
  2271.     >in @ -> sv_>in ^comp_class -> sv_^class  rec? -> sv_rec?
  2272.     Mword  count  " {" s=
  2273.     NIF                            \ we've got a name for the record
  2274.         true -> rec?            \ must do this before defining the name "object"
  2275.         sv_>in  >in !
  2276.         ['] object  ivDef
  2277.         sv_rec? -> rec?  sv_^class -> ^comp_class
  2278.         gobble{                    \ "{" must follow
  2279.     THEN
  2280. ;
  2281.  
  2282.  
  2283. : }RECORD
  2284.     131 ?pairs  rstRec
  2285.     ['] } >body !  ;
  2286.  
  2287.  
  2288. : RECORD{
  2289.     ?class                        \ must be compiling a class
  2290.     ['] } >body @                    \ save old action for "}"
  2291.     ['] }record  -> }            \ "}" will now be same as }record
  2292.     svRec                        \ save parameters for any existing record/union
  2293.     131                            \ for ?pairs
  2294.     true -> rec?  false -> union?  ;
  2295.  
  2296. : RECORD
  2297.     ?handle_name
  2298.     record{  ;
  2299.  
  2300. : 68k_record{    record{  ;        \ we need to distinguish on the PowerPC
  2301. : 68k_record    record   ;
  2302.  
  2303.  
  2304. : }UNION
  2305.     132 ?pairs
  2306.     unionOffs  ^comp_class dfa w!    
  2307.     rstRec
  2308.     ['] } >body !  ;                \ restore old action for "}"
  2309.  
  2310. : UNION{
  2311.     ?class                        \ must be compiling a class
  2312.     ['] } >body @                    \ save old action for "}"
  2313.     ['] }union  -> }            \ "}" will now be same as }union
  2314.     svRec                        \ save record/union parameters
  2315.     132                            \ for ?pairs
  2316.     true -> rec?  true -> union?
  2317.     ^comp_class dfa w@ -> unionOffs  ;
  2318.  
  2319.  
  2320. : UNION
  2321.     ?handle_name
  2322.     union{  ;
  2323.  
  2324.  
  2325. (*        =================  Static ivars ====================
  2326.  
  2327. Syntax:
  2328.  
  2329.     static
  2330.    {    var        v1
  2331.         int        i1
  2332.         string    s
  2333.    }
  2334.  
  2335. Or you can use  static{ ...  } if you prefer.
  2336.  
  2337. These are like static class variables in C++ - they belong to the class,
  2338. not the object, and thus are shared by all objects of the class.  We
  2339. allocate each ivar in the dictionary right after its ivar header.
  2340. *)
  2341.  
  2342. : }STATIC
  2343.     133 ?pairs
  2344.     ['] } >body !                    \ restore old action for "}"
  2345.     false -> static?  ;
  2346.  
  2347.  
  2348. : STATIC{
  2349.     ?class                        \ must be compiling a class
  2350.     ['] } >body @                \ save old action for "}"
  2351.     ['] }static  -> }            \ "}" will now be same as }static
  2352.     133                            \ for ?pairs
  2353.     true -> static?  ;
  2354.  
  2355. : STATIC
  2356.     gobble{  static{  ;
  2357.  
  2358.  
  2359. \            ==========================================
  2360.  
  2361. \ CL1 is our first cleanup word - called on an abort.  Resets things
  2362. \  to normal.  Later cleanup words do their special stuff, then call CL1.
  2363.  
  2364. : CL1
  2365.     (;cl)  clrComp  ['] (}) -> }
  2366.     resetTemps  false -> rec?  false -> union?
  2367.     false -> localSect?
  2368.     false -> compinline?
  2369.     0 -> extraFind
  2370.     0 -> bufPtr
  2371.     DP hiDP umax  -> DP
  2372.     false -> case_in_names?
  2373. ;
  2374.  
  2375. ' cl1  -> abortVec
  2376.  
  2377.  
  2378. load Struct
  2379.  
  2380. \            ==========================================
  2381.  
  2382. (* Normally we don't get here.  In order to do various tests on classes,
  2383.  we comment out the  <" Struct  and run these torture tests:
  2384. *)
  2385.  
  2386. : ?CHK    <> abort" check FAILED!!!"  ;    \ error if something doesn't
  2387.                                         \  give what we expect
  2388.  
  2389.  
  2390. :class    VAR    super{ object }
  2391.  
  2392.     4 bytes data
  2393.  
  2394. :m CLEAR:
  2395.     inline{ 0 ^base !}  ;m
  2396. \    0 ^base !  ;m
  2397.  
  2398. :m GET:
  2399.     inline{ ^base @}  ;m
  2400. \    ^base @  ;m
  2401.  
  2402. :m PUT:
  2403.     inline{ ^base !}  ;m
  2404. \    ^base !  ;m
  2405.  
  2406. :m GETT:    ^base @  ;m
  2407.     
  2408. :m PUTT:    ^base !  ;m
  2409.  
  2410. :m +:
  2411.     inline{ ^base +!}  ;m
  2412. \    ^base +!  ;m
  2413. :m -:
  2414.     inline{ ^base -!}  ;m
  2415. \    ^base -!  ;m
  2416. :m ->:
  2417.     inline{ @ ^base !}  ;m
  2418. \    chksame  get: var  put: self  ;m
  2419.  
  2420. :m TEST:        db  ;m
  2421.  
  2422. mlocal LOCTEST:  { aa \ bb cc -- }
  2423.  
  2424. :m AAA:    aa -> bb ;m
  2425.  
  2426. :mloc  LOCTEST:
  2427.     aaa: self  cc -> bb  1234 drop ;mloc
  2428.  
  2429.  
  2430. :m PRINT:
  2431.     ^base @  .  ;m
  2432.  
  2433. :m CLASSINIT:    $ 123  put: self  ;m
  2434.  
  2435. ;class
  2436.  
  2437.  
  2438. :class    BYTE    super(  object  )
  2439.  
  2440.     1 bytes data
  2441.  
  2442. :m CLEAR:
  2443.     inline{ 0 obj c!}
  2444.     0 ^base c!  ;m
  2445.  
  2446. :m GET:
  2447.     inline{ obj c@x}
  2448.     ^base c@x  ;m
  2449.  
  2450. :m UGET:
  2451.     inline{ obj c@}
  2452.     ^base c@  ;m
  2453.  
  2454. :m PUT:
  2455.     inline{ obj c!}
  2456.     ^base c!  ;m
  2457.  
  2458. :m ->:
  2459.     inline{ c@ obj c!}
  2460.     chksame  c@  put: self  ;m
  2461.  
  2462. :m PRINT:
  2463.     ^base c@  .        ;m
  2464.  
  2465. :m CLASSINIT:    9 put: self  ;m
  2466.  
  2467. ;class
  2468.  
  2469. \ some very simple testing, to start with:
  2470.  
  2471. var        aVar
  2472. byte    aByte
  2473.  
  2474. 987 avar !
  2475. get: avar    987 ?chk
  2476. : q  get: avar  ;
  2477. q            987 ?chk
  2478.  
  2479.  
  2480. :class    BOOL    super(  byte  )
  2481.  
  2482. :m GET:
  2483.     inline{ obj c@x}
  2484.     ^base c@x  ;m
  2485.  
  2486. :m PUT:
  2487.     inline{ 0<> obj c!}
  2488.     0<>  ^base c!  ;m
  2489.  
  2490. :m SET:
  2491.     inline{ true obj c!}
  2492.     true ^base c!  ;m
  2493.  
  2494. :m PRINT:
  2495.     get: self  IF  ." true"  ELSE  ." false"  THEN  ;m
  2496.  
  2497. :m CLASSINIT:    clear: self  ;m
  2498.  
  2499. ;class
  2500.  
  2501.  
  2502. :class    BARRAY  super{ object }  1 indexed
  2503.  
  2504. :m  AT:        \ ( index -- n )
  2505.     inline{ ix c@}
  2506.     ^elem1  c@  ;m
  2507.  
  2508. :m  TO:        \ ( n index -- )
  2509.     inline{ ix c!}
  2510.     ^elem1  c!  ;m
  2511.  
  2512.  
  2513. :m ^ELEM:    \ ( index -- addr )
  2514.     inline{ ix}
  2515.     ^elem1  ;m
  2516.  
  2517. :m FILL:    \ ( value -- )  Fills all elements with value.
  2518.     idxbase  limit 2*  bounds
  2519.     ?DO  dup  i c!  LOOP  drop  ;m
  2520.  
  2521. :m WIDTH:    1  ;m        \ Faster than the default in Object
  2522.  
  2523. :m GETELEM:    \ ( addr -- n )  Fetches one element at addr
  2524.     c@x  ;m
  2525.  
  2526. ;class
  2527.  
  2528.  
  2529. +echo
  2530.  
  2531. \ bug test here:
  2532.  
  2533. :class    INDEXED-OBJ  super{ object }
  2534.  
  2535. :m ^ELEM:    ^elem  ;m
  2536.  
  2537. :m LIMIT:    limit  ;m
  2538.  
  2539. :m WIDTH:    idxbase  6 -  w@  ;m
  2540.  
  2541. :m IXADDR:    idxbase  ;m
  2542.  
  2543. :m CLEARX:    \ Erases indexed area.
  2544.     idxbase  limit  width: self  *  erase  ;m
  2545.  
  2546. :m CLASSINIT:    clearX: self  ;m
  2547.  
  2548. ;class
  2549.  
  2550.  
  2551. :class    WARRAY  super{ indexed-obj }  2 indexed
  2552.  
  2553. :m AT:        \ ( index -- n )
  2554.     inline{ ^elem w@x}  ;m
  2555.  
  2556. :m TO:        \ ( n index -- )
  2557.     inline{ ^elem w!}  ;m
  2558.  
  2559. ;class
  2560.  
  2561.  
  2562. :class  TRIGTABLE    super{ wArray }
  2563.  
  2564.     3    wArray  AXISVALS
  2565. ;class
  2566.  
  2567. 10 trigtable ttt
  2568.  
  2569. : q  9 at: ttt  ;
  2570.  
  2571.  
  2572. \ Testing static and public ivars
  2573.  
  2574.  
  2575. :class SIVTEST  super{ var }
  2576. public
  2577. static
  2578. {    var        V1
  2579.     bool    B1
  2580.     byte    B2
  2581. 10    barray    BB
  2582. }
  2583.     bool    BLOC
  2584.     var        VLOC
  2585.     
  2586. :m QQ:    get: v1  get: b1  get: b2 4 at: bb
  2587.         get: vloc  ;m
  2588.  
  2589. :m CLASSINIT:
  2590.         32 put: v1  set: b1  33 put: b2  34 4 to: bb
  2591.         set: bloc  34 put: vloc  ;m
  2592. ;class
  2593.  
  2594. sivtest zzz
  2595. sivtest sss
  2596. objPtr myop  class_is sivtest
  2597.  
  2598. : QQQ        addr: ivar> v1 in_class sivtest  drop
  2599.             get: ivar> b2 in_class sivtest
  2600.             get: ivar> v1 in_class sivtest
  2601.             sss get: ivar> bloc in class_as> sivtest  ;
  2602.  
  2603. qqq
  2604. -1    ?chk
  2605. 32    ?chk
  2606. 33    ?chk
  2607.  
  2608.  
  2609. :class HAHA  super{ object }
  2610.  
  2611.     sivtest    IVsss
  2612.     
  2613. :m QQ:      get: ivar> vloc IN ivsss  ;m
  2614. ;class
  2615.  
  2616. haha hh
  2617.  
  2618. qq: hh
  2619. 34 ?chk
  2620.  
  2621. : WWW  temp { sivtest mysiv }
  2622.     get: ivar> vloc IN mysiv
  2623.     mysiv -> myop
  2624.     get: ivar> vloc IN myop  ;
  2625.     
  2626. www
  2627. 34 ?chk
  2628. 34 ?chk
  2629.  
  2630.     get: ivar> vloc IN zzz
  2631. 34 ?chk
  2632.  
  2633.  
  2634. \ Testing record{
  2635.  
  2636. :class VAR+ super{ var }
  2637.  
  2638. :m QQ:    get: [self]        \ should make class general
  2639.         get: [ self ]    \ shouldn't give any error
  2640. ;m
  2641.  
  2642. ;class
  2643.  
  2644. var+ VVV
  2645. qq: vvv        \ no need for ?chk since it will give its own error
  2646.  
  2647.  
  2648. :class RECTEST super{ object }
  2649.     var    vv
  2650.     record RR
  2651.     {        var        v1
  2652.             bool    b1
  2653.         3    barray  bbb
  2654.             byte    dummyToMakeAddrOdd
  2655.         union {    byte    b2
  2656.                 var        v2
  2657.                 record    {    byte bb1
  2658.                             byte bb2    }
  2659.             }
  2660.             var        v3
  2661.     }
  2662.     
  2663. :m TEST:
  2664.     get: v1  put: b1  get: b2  get: v2  get: bb1  get: bb2  get: v3
  2665. ;m
  2666. ;class
  2667.  
  2668. recTest rrr
  2669. test: rrr
  2670. $ 123        ?chk
  2671. 0            ?chk
  2672. 9            ?chk
  2673. $ 09000123    ?chk
  2674. 9            ?chk
  2675. $ 123        ?chk
  2676. $ 123        ?chk
  2677.  
  2678. rrr $ 24 + @  $ 09000123  ?chk
  2679.  
  2680. \ Testing temp objects
  2681.  
  2682. : q
  2683. temp
  2684. {    var    v1
  2685.      var    v2
  2686. }temp
  2687.     v1 v2
  2688.     get: v1  get: v2  ;
  2689.  
  2690. q
  2691. $ 123    ?chk
  2692. $ 123    ?chk
  2693. 2drop
  2694.  
  2695. :class INT  super( object )
  2696.  
  2697.     2    bytes    data
  2698.  
  2699. :m CLEAR:
  2700.     inline{  0 obj !  }
  2701.     0 ^base !  ;m
  2702.  
  2703. :m UGET:
  2704.     inline{  ^base w@  }
  2705.     ^base w@  ;m
  2706.  
  2707. :m GET:
  2708.     inline{  obj w@x  }
  2709.     ^base w@x  ;m
  2710.  
  2711. :m IPUT:    ^base w!  ;m
  2712.  
  2713. :m DISP:
  2714.     inline{  obj 2+ @  }  ;m
  2715.  
  2716. :m PUT:
  2717.     inline{  obj w!  }
  2718.     ^base  w!  ;m
  2719.  
  2720. :m MOVE:
  2721.     inline{  obj 4+ w@  obj w!  }  ;m
  2722.  
  2723.  
  2724. :m +:    inline{  obj w+!  }
  2725.     ^base  w+!  ;m
  2726.  
  2727. :m ->:
  2728.     inline{  w@ obj w!  }
  2729.     chksame  1234 drop  get: int  put: self  ;m
  2730.  
  2731. :m ++>:
  2732.     inline{  w@ obj w+!  }
  2733.     chksame  uget: int  +: self  ;m
  2734.  
  2735. :m .ID:    ." haha"  ;m
  2736.  
  2737. :m TEST:
  2738.     1234 drop  .id: super  ;m
  2739.  
  2740. :m CLASSINIT:    $ 456 put: self  ;m
  2741.  
  2742. ;class
  2743.  
  2744.  
  2745. :class CC  super{ byte int var bool }
  2746.  
  2747. :m TEST:
  2748.     uget: self        \ offs should be 0
  2749.     +: self                \ offs should be 4
  2750.     set: self  ;m        \ offs should be A
  2751.  
  2752. :m TEST1:
  2753.     set: self
  2754.     get: super> bool    \ should get -1
  2755.     get: super
  2756. ;m
  2757.     
  2758. :m classinit:  ( db )  ;m
  2759.  
  2760. ;class
  2761.  
  2762. cc CCC
  2763.  
  2764. ccc @        $ 0900fff6    ?chk
  2765. ccc 4+ @    $ 0456fff2    ?chk
  2766. ccc 8 + @    $ 123        ?chk
  2767.  
  2768.  
  2769. :class STRANGE  super{ object }
  2770.     var VV
  2771.     byte BB
  2772. :m GET:  get: vv  get: bb  ;m
  2773. :m PUT:  put: bb  put: vv  ;m
  2774.  
  2775. ;class
  2776.  
  2777.  
  2778. :class    ARRAY    super(  object  )    4 indexed
  2779.  
  2780. \ 8 bytes data        \ Comment out to check collapsing of embedded objs
  2781.  
  2782. :m ^ELEM:    \ ( index -- addr )
  2783.     ^elem4  ;m
  2784.  
  2785. :m QQQ:    inline{ ix }  ;m
  2786.  
  2787. :m  AT:        \ ( index -- n )
  2788.     inline{ ix @ }
  2789.     ^elem4  @  ;m
  2790.  
  2791. :m  ATT:    ^elem  @  ;m        \ As for AT:, but not inline
  2792.                 \  and uses unoptimized ^elem
  2793.  
  2794. :m  TO:        \ ( n index -- )
  2795.     inline{  ix !  }
  2796.     ^elem4  !  ;m
  2797.  
  2798. :m  +TO:        \ ( n index -- )
  2799.     inline{ ix +! }
  2800.     ^elem4  +!  ;m
  2801.  
  2802. :m -TO:        \ ( n index -- )
  2803.     inline{ ix -! }
  2804.     ^elem4  -!  ;m
  2805.  
  2806. :m FILL:        \ ( value -- )  Fills all elements with value.
  2807.     idxbase  limit 4*  bounds
  2808.     DO  dup  i !  4 +LOOP  drop  ;m
  2809.  
  2810. :m EXEC:        \ ( index -- )  execute the cfa, by jumping there.
  2811.     inline{ ix ex}
  2812.     ^elem: self  execute  ;m
  2813.  
  2814. :m TEST:
  2815.     exec: self  ;m
  2816.  
  2817. :m ATEST:
  2818.     1 at: self  ;m
  2819.  
  2820. ;class
  2821.  
  2822.  
  2823. :class MULT    super( var int array )
  2824.  
  2825. :m MTEST:    uget: super  999 1 to: self  ;m
  2826. :m MAT:        at: self  ;m
  2827. ;class
  2828.  
  2829.  
  2830. objPtr    OO    class_is mult
  2831. objPtr    OOO    class_is int
  2832.  
  2833. :class IVXX    super( object )
  2834.     10 bytes data2
  2835.     int    i1
  2836.     int    i2
  2837.     130 bytes qqqq        \ Include to check >128 distance
  2838.                         \  index addressing of array qwert
  2839.     9 array qwert
  2840.  
  2841. :m ITEST:
  2842.     get: i1  uget: i2  66 put: i2
  2843.     99 3 to: qwert  1234 drop  3 at: qwert
  2844.     addr: i2  ['] ooo !  ;m
  2845.  
  2846. :m GETQWERT:
  2847.     addr: qwert  ;m
  2848. ;class
  2849.  
  2850. int ii
  2851. 3 mult    mm
  2852. ivxx    iv
  2853.  
  2854. mm -> oo
  2855.  
  2856. itest: iv
  2857.  
  2858. $ 63    ?chk
  2859. $ 456    ?chk
  2860. $ 456    ?chk
  2861.  
  2862. mtest: mm
  2863. $ 456    ?chk
  2864.  
  2865. 88 iput: mm        \ Note: get: mm will bind to the var, but uget: mm
  2866.                 \ will bind to the int and give 88.
  2867.  
  2868. get: mm  $ 123    ?chk
  2869. uget: mm 88        ?chk
  2870.  
  2871.  
  2872. \ A further test - Doug H found this bug:
  2873.  
  2874. :class  POINT    super{ object }
  2875.     int    Y        \ Vertical coordinate
  2876.     int    X        \ Horizontal  coordinate
  2877. ;class
  2878.  
  2879.  
  2880. :class  RECT  super{ object }
  2881.     point    TOPL
  2882.     point    BOTR
  2883. ;class
  2884.  
  2885. :class test1 super{ object }
  2886.  
  2887.     20 array a
  2888.  
  2889. :m classinit:
  2890.     55 0 to: a ;m
  2891.  
  2892. :m to:  to: a ;m
  2893.  
  2894. :m at:  at: a ;m
  2895.  
  2896. ;class
  2897.  
  2898. :class test3 super{ rect test1 }
  2899. :m classinit:
  2900.     [ 1 -> supers_to_skip ]  classinit: super
  2901. ;m
  2902. ;class
  2903.  
  2904. test3 t3
  2905.  
  2906. : q            getqwert: iv  3 swap at: **  ;    \ Should give 99
  2907. : qq        1 at: mm ;                        \ Should give 999
  2908. : qqq        1 mat: mm  ;                    \ Should give 999
  2909. : qqqq        1 mm at: mult  ;                \ Should give 999
  2910. : z            1 mm at: **  ;                    \ Should give 999
  2911. : zz        1 mm at: array ;                \ Should fail
  2912. : y            1 at: oo   ;                    \ Should give 999
  2913. : yy        1 mat: oo  ;                    \ Should give 999
  2914. : yyy        uget: mm  ;                        \ Should optimize & give 88
  2915. : yyyy        addr: mm  addr: oo  ;            \ Both numbers shd be same
  2916. : yyyyy        uget: ooo  ;                    \ Should give 66
  2917. : yyyyyy    0 at: t3  ;                        \ Should give 55
  2918.  
  2919.  
  2920. q         99    ?chk
  2921. qq         999    ?chk
  2922. qqq     999    ?chk
  2923. qqqq     999 ?chk
  2924. z         999    ?chk
  2925. y         999    ?chk
  2926. yy         999    ?chk
  2927. yyy     88    ?chk
  2928. yyyy        ?chk
  2929. yyyyy     66    ?chk
  2930. yyyyyy    55    ?chk
  2931.  
  2932. \ torture tests WORKED!  INCREDIBLE!!  CONGRATULATIONS!!!
  2933. \ (but remember to check that ZZ gives a "can't use indexed method" error)
  2934. key!
  2935.